Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seeding data: The seed entity for entity type 'X' cannot be added because there was no value provided for the required property 'Id'. #11776

Closed
bugproof opened this issue Apr 23, 2018 · 14 comments

Comments

@bugproof
Copy link

bugproof commented Apr 23, 2018

I'm using EntityFrameworkCore 2.1.0-preview2-final

I try to seed data, this works:

modelBuilder.Entity<Dog>().HasData(new Dog[]
{
    new Dog() { Id = 1, Name = "Lucy", Age = 2 },
    new Dog() { Id = 2, Name = "Bella", Age = 3 },
    new Dog() { Id = 3, Name = "Daisy", Age = 5 },
});

this doesn't:

modelBuilder.Entity<Dog>().HasData(new Dog[]
{
    new Dog() { Name = "Lucy", Age = 2 },
    new Dog() { Name = "Bella", Age = 3 },
    new Dog() { Name = "Daisy", Age = 5 },
});

And I get:

The seed entity for entity type 'X' cannot be added because there was no value provided for the required property 'Id'.

public class BaseEntity
{
    public int Id { get; set; }
}

public class Dog : BaseEntity
{
    public string Name { get; set; }
    public int Age { get; set; }
}

I expected it to work even when skipping Id. Is it auto increment by default?

I wanted to get it working with Bogus:

var dogsIds = 0;
var testDogs = new Faker<Dog>().RuleFor(p => p.Name, f => f.Name.FirstName())
    .RuleFor(p => p.Age, f => DateTime.Now.Year - f.Person.DateOfBirth.Year)
    .RuleFor(p => p.Id, f => dogsIds++);

var dogs = testDogs.Generate(100);

modelBuilder.Entity<Dog>().HasData(dogs.ToArray());
@bugproof bugproof changed the title The seed entity for entity type 'X' cannot be added because there was no value provided for the required property 'Id'. Seeding data: The seed entity for entity type 'X' cannot be added because there was no value provided for the required property 'Id'. Apr 23, 2018
@smitpatel
Copy link
Member

For data in HasData method, you need to provide values for auto-generated key properties.

@bugproof
Copy link
Author

bugproof commented Apr 23, 2018

@smitpatel I did exactly that, using Bogus but it doesn't seem to work with HasData. I had to use the workaround described in dotnet/aspnetcore#2188

I wanted to generate some random data to test my API with.

@smitpatel
Copy link
Member

@enemyofthedawn - Make sure you generate some random, unique values for key column too. 😀

@ajcvickers
Copy link
Member

@enemyofthedawn Just to elaborate on why store-generated values are not supported here. The idea of having data in the model is that when the model is evolved, the seed data in the database is evolved along with it. But for that to work, each entity in the model needs to have a well-known key value such that it can be found and updated later. Feel free to use more traditional seeding mechanisms for, for example, tests that just need to initialize some data into an empty database.

@bugproof
Copy link
Author

@ajcvickers I'm not sure if I understood. Do you mean that generated objects like above won't work with HasData?

@ajcvickers
Copy link
Member

@enemyofthedawn No, I'm just saying that the key values must be specified.

@bugproof
Copy link
Author

bugproof commented Apr 24, 2018

It worked with Bogus, I realized I had to start incrementing from 1, and Bogus did it from 0.

@ajcvickers It seems like HasData should be used more to fill database with some production data rather than test data. Right?

It seems like generating random data using HasData will modify/add data in each migration I add unless I add some check if the data exists before.

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            int i = 1;
            var faker = new Faker<Order>()
                .RuleFor(o => o.Name, f => f.Commerce.ProductName())
                .RuleFor(o => o.Id, f => i++);

            var orders = faker.Generate(10);

            modelBuilder.Entity<Order>().HasData(orders.ToArray());
        }

first migration:

 migrationBuilder.InsertData(
                table: "Orders",
                columns: new[] { "Id", "Name" },
                values: new object[,]
                {
                    { 1, "Licensed Cotton Shirt" },
                    { 2, "Rustic Concrete Pizza" },
                    { 3, "Handcrafted Metal Keyboard" },
                    { 4, "Licensed Wooden Chicken" },
                    { 5, "Awesome Metal Pizza" },
                    { 6, "Practical Metal Chair" },
                    { 7, "Handmade Steel Gloves" },
                    { 8, "Refined Soft Chair" },
                    { 9, "Sleek Plastic Salad" },
                    { 10, "Ergonomic Steel Mouse" }
                });

2nd migration:

migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 1,
                column: "Name",
                value: "Small Steel Chips");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 2,
                column: "Name",
                value: "Incredible Fresh Sausages");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 3,
                column: "Name",
                value: "Awesome Plastic Fish");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 4,
                column: "Name",
                value: "Awesome Metal Sausages");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 5,
                column: "Name",
                value: "Incredible Soft Sausages");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 6,
                column: "Name",
                value: "Refined Granite Chair");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 7,
                column: "Name",
                value: "Generic Granite Tuna");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 8,
                column: "Name",
                value: "Generic Wooden Fish");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 9,
                column: "Name",
                value: "Sleek Soft Towels");

            migrationBuilder.UpdateData(
                table: "Orders",
                keyColumn: "Id",
                keyValue: 10,
                column: "Name",
                value: "Unbranded Wooden Pizza");

@TheBauwssss
Copy link

Can the error message

System.InvalidOperationException: The seed entity for entity type '' cannot be added because there was no value provided for the required property 'Id'.

be changed to

System.InvalidOperationException: The seed entity for entity type '' cannot be added because there was no value provided for the required property 'Id'. Please note that 0 is not a valid value for the Id field. Please start incrementing from 1.

I just wasted a whole day trying to figure out why it was not working only to discover that I needed to start incrementing from 1 instead of 0.

@ajcvickers
Copy link
Member

@TheBauwssss Filed #12399

@shawty
Copy link

shawty commented Nov 15, 2018

I know this has been closed, but I just wanted to add a warning for anyone finding this thread while looking for an answer to this on PostgreSQL.

When PostgreSQL generates ID columns with auto increment, it does so using a "Sequence".

With a "Sequence" the column in the table is still just a plain old integer, and as such can be written to without problems.

In the case of data seeding, if you add a record in with a manual ID = 1 for example, the first NEW record you try to insert using EF after that will fail.

It fails beacuse setting a value on the integer column does not update the underlying sequence, so the next record you try to insert using EF, get's inserted with a 1 in the ID, which causes a conflict.

The Sequence then increments, and since your now on 2+ your inserts will now all work.

If you add say 4 records in your seed data, then you'll get 4 fails before things start to work.

You can set the sequence start number etc using the fluent API, but you have to do that individually for every single column that's used as an ID, and you have to set it specifically depending on your seed data.

FWIW: This is actually not a bug, it's the way PG has always worked :-)

@roji
Copy link
Member

roji commented Nov 16, 2018

@shawty this is a known issue, see npgsql/efcore.pg#367. The easy workaround for this problem is to seed negative values for your IDs, and since your sequence (by default) starts at one there will be no conflict.

It's also worth looking at the new PostgreSQL 10 identity columns as a better alternative to PostgreSQL serial columns (see the Npgsql docs for this).

@bugproof
Copy link
Author

bugproof commented Dec 17, 2018

Just like @roji said in PostgreSQL setting id explicitly causes problems. This is explained here

Also using PostgreSQL 10 identity columns doesn't help to solve this

@samodelkinvv
Copy link

samodelkinvv commented Dec 28, 2018

Bruh...Same here.

The seed entity for entity type 'Entity' cannot be added because a non-zero value is required for property 'Id'.

What's the point of ModelBuilder if I should manually add autogenerated properties?!
End up writing custom Seeder like I would create it with EF

Entity ent = new Entity(){
...my fields
}
dbContext.SaveChanges();

Then what's the point in ModelBuilder.HasData() in the first place ?!

@robertmarriott
Copy link

I agree with @samodelkinvv. Coming from Django and Laravel, I must say that ASP MVC and Entity Framework Core aren't exactly user-friendly. And the explanations as to why things are done this way leave a lot to be desired. I'm hoping this will change as Core evolves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants