I am trying to seed an user entity in my database. The User
entity has an owend property EmailPermissions
.
When I run the command
In my scenario I wanted the owned-type property to be auto-initialed in the parent class:
public class User
{
EmailPermissions _EmailPermissions;
public EmailPermissions
{
get => _EmailPermissions ??= new EmailPermissions();
set => _EmailPermissions = value;
}
}
When I tried to add seed data I got that nasty exception.
The solution was to pass the User
as anonymous type in its HasData
call.
Currently this information is missing from the documentation (tracked by #710: Document how to seed owned types). It's explained by EF Core team (with example) in the #12004: Problem seeding data that contains owned type thread:
Owned types must be seeded with a
HasData
call after theOwnsOne
call. Also, since owned types by convention have a primary key generated in shadow state, and since seed data requires keys to be defined, then this requires use of an anonymous type and setting the key.
which is basically what the exception message is telling you.
Following the advice, you should remove the instantiation of the EmailPermissions
property from the constructor and add a seeding code like this:
builder.Entity<User>().OwnsOne(e => e.EmailPermissions).HasData(
new
{
UserId = "37846734-172e-4149-8cec-6f43d1eb3f60",
// other properties ...
}
);
Quite annoying and error prone due to the need to know the shadow PK name and the usage of an anonymous type. As the same member mentioned
Note that this would become easier if navigations were supported for seeding, which is tracked by #10000: Data Seeding: Add support for navigations
If you want to avoid using an anonymous type to specify the shadow property keys, you can declare them explicitly in your model class and configure them with the Fluent API as keys. This way you don't have to guess the property names and it's less error-prone.
If the name supplied to the Property method matches the name of an existing property (a shadow property or one defined on the entity class), then the code will configure that existing property rather than introducing a new shadow property. Source
I had the same issue seeded my data at startup. Here is the link to the github issue.
Thank Ivan Stoev's answer. i add some more code to easy to imagine. this is code of seed data function base on example.
Example: Entity XXX => PK will be XXXId
private void SeedUser(ModelBuilder builder)
{
builder.Entity<User>(b =>
{
b.HasData(new User
{
Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
Email = "foo@foo.foo",
UserName = "foo@foo.foo",
// more properties of User
});
b.OwnsOne(e => e.EmailPermissions).HasData(new
{
UserId = "37846734-172e-4149-8cec-6f43d1eb3f60",
Newsletter = true,
PromotionalOffers = true,
PrestationReminders = true,
PrestationOffers = true
});
});
}