Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Seed entity with owned property

Tags:

I am trying to seed an user entity in my database. The User entity has an owend property EmailPermissions.

When I run the command

dotnet ef migrations add Initial;

I get the error

The seed entity for entity type 'User' cannot be added because it has the navigation 'EmailPermissions' set. To seed relationships you need to add the related entity seed to 'EmailPermissions' and specify the foreign key values {'UserId'}.

but since EmailPermissions is an owned entity I didn't give it an explicit UserId property, meaning I can't seed it separately in the database.

the entity

public sealed class User : IdentityUser {     public User()     {         EmailPermissions = new EmailPermissions();     }      /* [..] */      public string TemporaryEmailBeforeChange { get; set; }     public bool IsEmailAwaitingUpdate { get; set; }     public EmailPermissions EmailPermissions { get; set; }     public ICollection<Registeration> Registerations { get; set; }      /* [..] */  }  [Owned] public class EmailPermissions {     /* [..] */      public bool Newsletter { get; set; }     public bool PromotionalOffers { get; set; }     public bool PrestationReminders { get; set; }     public bool PrestationOffers { get; set; } } 

The seeding call

private void SeedUser(ModelBuilder builder) {     builder.Entity<User>().HasData(         new User         {             Id = "37846734-172e-4149-8cec-6f43d1eb3f60",             Email = "[email protected]",             UserName = "[email protected]",             PasswordHash = "AQAAAAEAACcQAAAAEIytBES+jqKH9jfuY3wzKyduDZruyHMGE6P+ODe1pSKM7BuGjd3AIe6RGRHrXidRsg==",             SecurityStamp = "WR6VVAGISJYOZQ3W7LGB53EGNXCWB5MS",             ConcurrencyStamp = "c470e139-5880-4002-8844-ed72ba7b4b80",             EmailConfirmed = true         }); }    

If I remove the instantiation of the EmailPermissions property from the constructor I get the following error instead

The entity of type 'User' is sharing the table 'AspNetUsers' with entities of type 'EmailPermissions', but there is no entity of this type with the same key value that has been marked as 'Added'.

How can I seed a user via the .HasData method when it has an owned property ?

like image 657
Mathieu VIALES Avatar asked Jun 14 '18 16:06

Mathieu VIALES


People also ask

What is an owned entity type?

The entity containing an owned entity type is its owner. Owned entities are essentially a part of the owner and cannot exist without it, they are conceptually similar to aggregates. This means that the owned entity is by definition on the dependent side of the relationship with the owner.

Was created in shadow state because a conflicting property?

InsuranceSubjectID1' was created in shadow state because a conflicting property with the simple name 'InsuranceSubjectID' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type.

What seeded data?

Database seeding is populating a database with an initial set of data. It's common to load seed data such as initial user accounts or dummy data upon initial setup of an application.


2 Answers

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 the OwnsOne 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

like image 132
Ivan Stoev Avatar answered Sep 22 '22 13:09

Ivan Stoev


Thank Ivan Stoev's answer. i add some more code to easy to imagine. this is code of seed data function base on example.

  • First adding data of User.
  • After that add data of owned object.
  • Data of owned object have to be anonymous because PK will request. This PK will not appear in database. Name should be entity name + Id

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 = "[email protected]",             UserName = "[email protected]",             // 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         });     }); } 
like image 43
Long Pham Avatar answered Sep 22 '22 13:09

Long Pham