Seeding Data - Owned Type without Id

1.2k Views Asked by At

I have an Entity describing an organization, including its postal address. The Address is stored in a property (PostalAddress) and all properties of Organization is stored and flattened in the same database table. The configuration compiles and the migration is applied without any problems - if I don't seed data. I have tested standard CRUD in Razor Pages - no problem.

When adding a Seed in OnModelCreating I get an error when compiling.

The seed entity for entity type 'Organization.PostalAddress#PostalAddress' cannot be added because no value was provided for the required property 'OrganizationId'.

The message confuses me, as neither Organization, nor PostalAddress have an OrganizationId property. Neither does a shadow property exist in the db. Any ideas of what causes the problem?

public abstract class BaseEntity<TEntity>
{
    [Key] public virtual TEntity Id { get; set; }
}

public class MyOrganization : BaseEntity<long>
{
    public string Name { get; set; }                    // Name of the Organization
    public PostalAddress PostalAddress { get; set; }    // Postal address of the Organization
    public string Email { get; set; }                   // Email of the Organization
}

public class PostalAddress
{
    public string StreetAddress1 { get; set; }  // Address line 1
    public string ZipCode_City { get; set; }    // Zip code
    public string Country { get; set; }         // Country
}
public void Configure(EntityTypeBuilder<Organization> builder)
{
    builder
        .ToTable("Organizations")
        .HasKey(k => k.Id);

    // Configure PostalAddress owned entity
    builder
        .OwnsOne(p => p.PostalAddress, postaladdress =>
        {
            postaladdress
                .Property(p => p.StreetAddress1)
                .HasColumnName("StreetAddress1")
                .HasColumnType("nvarchar(max)")
                .IsRequired(false);
            postaladdress
                .Property(p => p.ZipCode_City)
                .HasColumnName("ZipCode_City")
                .HasColumnType("nvarchar(max)")
                .IsRequired(false);
            postaladdress
                .Property(p => p.Country)
                .HasColumnName("Country")
                .HasColumnType("nvarchar(max)")
                .IsRequired(false);
        });
}

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

    builder
        .AddDBConfigurations();

    // Seed data
    builder
        .Entity<Organization>(b =>
        {
            b.HasData(new Organization 
            {
                Id = 1,
                Name = "Test Organization",
                Email = "[email protected]"
            });
            b.OwnsOne(e => e.PostalAddress)
                .HasData(new 
                { 
                    StreetAddress1 = "1600 Pennsylvania Avenue NW", ZipCode_City = "Washington, D.C. 20500", Country = "USA"
                });
        }); 
}
2

There are 2 best solutions below

0
Gert Arnold On BEST ANSWER

The exception message is enigmatic, but helpful. When you add OrganizationId to the seeding code for PostalAddress it works.

modelBuilder
    .Entity<Organization>(b =>
    {
        b.HasData(new Organization
        {
            Id = 1, // Here int is OK.
            Name = "Test Organization",
            Email = "[email protected]"
        });
        b.OwnsOne(e => e.PostalAddress)
            .HasData(new
            {
                OrganizationId = 1L, // OrganizationId, not Id, and the type must match.
                StreetAddress1 = "1600 Pennsylvania Avenue NW",
                ZipCode_City = "Washington, D.C. 20500",
                Country = "USA"
            });
    });

Probably some undocumented convention is involved here. Which makes sense: the owned type can be added to more entity classes and EF needs to know which one it is. One would think that Id should be enough, after all, the type is clearly added to b, the Organization. I think some inner code leaks into the public API here and one day this glitch may be fixed.

Note that the type of the owner's Id must match exactly, while the seeding for the type itself accepts a value that has an implicit conversion.

0
Mohamed Adel On

it caused by convention if you need to make one to one relationship you need to add virtual property of MyOrganization and add organizationId in postal address object when seeding data this also help you to configure one to one relation https://www.learnentityframeworkcore.com/configuration/one-to-one-relationship-configuration