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

Can't add migration for nested owned entities #32746

Closed
vyruz1986 opened this issue Jan 8, 2024 · 2 comments
Closed

Can't add migration for nested owned entities #32746

vyruz1986 opened this issue Jan 8, 2024 · 2 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@vyruz1986
Copy link

I'm failing to figure out how to configure data seeding on nested owned entities. I've read through #31373 and #14359 and am aware of the current issues about EFCore not being able to seed data on the top-level entity (tracked in #10000). What those issues have not yet discussed, is seeding data across nested owned types. I have made a minimal example (also attached below) which configures a Product > Price > MonetaryAmount relation.

Creating a migration without the data seeding works, and the generated SQL looks good in that it wants to create a single table with a column for each of the properties of the owned types, but when I add the HasData() statements and try to create a migration, I'm given the following error:

Unable to create a 'DbContext' of type ''. The exception 'The seed entity for entity type 'Product' cannot be added because it has the navigation 'Price' set. To seed relationships,  add the entity seed to 'Price' and specify the foreign key values {'ProductId'}.

Include your code

public class Product
{
    private Product() { }
    public Product(int id, Price price)
    {
        Id = id;
        Price = price ?? throw new ArgumentNullException(nameof(price));
    }

    public int Id { get; private init; }
    public Price Price { get; private init; }
}

public record Price
{
    private Price() { }

    public Price(MonetaryAmount amount)
    {
        Amount = amount ?? throw new ArgumentNullException(nameof(amount));
    }

    public MonetaryAmount Amount { get; }
}

public record MonetaryAmount
{
    public MonetaryAmount(decimal value, Currency currency)
    {
        if (!Enum.IsDefined(currency))
        {
            throw new ArgumentOutOfRangeException(nameof(currency), currency, "The currency is unsupported");
        }
        Value = value;
        Currency = currency;
    }

    public decimal Value { get; private set; }

    public Currency Currency { get; }
}

public enum Currency
{
    EUR = 978
}

public class ProductContext : DbContext
{
    public ProductContext()
    {
    }

    public ProductContext(DbContextOptions options) : base(options)
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlServer();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>(builder =>
        {
            builder.OwnsOne(p => p.Price, price =>
            {
                price.OwnsOne(p => p.Amount, priceAmount =>
                {
                    priceAmount.Property(p => p.Value)
                        .HasPrecision(2, 20)
                        .IsRequired();

                    priceAmount.Property(p => p.Currency)
                        .HasDefaultValue(Currency.EUR)
                        .IsUnicode(false)
                        .HasMaxLength(3)
                        .IsRequired();

                    priceAmount.HasData(new { PriceProductId = 1, Value = 1m, Currency = Currency.EUR });
                });

                price.HasData(new { ProductId = 1 });
            });

            builder.HasData(new Product(1, new Price(new MonetaryAmount(1, Currency.EUR))));
        });
    }
}

Full example available here:
EFCoreNestedOwnedTypesDataSeeding.zip

Stack traces

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at SixFour.Sub() in C:\Stuff\AllTogetherNow\SixFour\SixFour.cs:line 49
   at SixFour.Main() in C:\Stuff\AllTogetherNow\SixFour\SixFour.cs:line 54

Verbose output

Using assembly 'EFCoreNestedOwnedTypesDataSeeding'.
Using startup assembly 'EFCoreNestedOwnedTypesDataSeeding'.
Using application base 'C:\Users\ag\workspaces\poc\EFCoreNestedOwnedTypesDataSeeding\bin\Debug\net8.0'.
Using working directory 'C:\Users\ag\workspaces\poc\EFCoreNestedOwnedTypesDataSeeding'.
Using root namespace 'EFCoreNestedOwnedTypesDataSeeding'.
Using project directory 'C:\Users\ag\workspaces\poc\EFCoreNestedOwnedTypesDataSeeding\'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'EFCoreNestedOwnedTypesDataSeeding'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'ProductContext'.
Using context 'ProductContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create a 'DbContext' of type ''. The exception 'The seed entity for entity type 'Product' cannot be added because it has the navigation 'Price' set. To seed relationships,  add the entity seed to 'Price' and specify the foreign key values {'ProductId'}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
 ---> System.InvalidOperationException: The seed entity for entity type 'Product' cannot be added because it has the navigation 'Price' set. To seed relationships,  add the entity seed to 'Price' and specify the foreign key values {'ProductId'}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateData(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateData(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.Initialize(IModel model, Boolean designTime, IDiagnosticsLogger`1 validationLogger)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService(IInfrastructure`1 accessor, Type serviceType)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Unable to create a 'DbContext' of type ''. The exception 'The seed entity for entity type 'Product' cannot be added because it has the navigation 'Price' set. To seed relationships,  add the entity seed to 'Price' and specify the foreign key values {'ProductId'}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the involved property values.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

Include provider and version information

EF Core version:
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Windows 11 23H2 build 22631.2861
IDE: VSCode 1.85.1

@ajcvickers
Copy link
Member

@vyruz1986 This requires using an anonymous type, since the Product type can't be created publicly without setting the navigation property. Something like this:

modelBuilder.Entity<Product>(builder =>
{
    builder.OwnsOne(p => p.Price, price =>
    {
        price.OwnsOne(p => p.Amount, priceAmount =>
        {
            priceAmount.Property(p => p.Value)
                .HasPrecision(2, 20)
                .IsRequired();
            priceAmount.Property(p => p.Currency)
                .HasDefaultValue(Currency.EUR)
                .IsUnicode(false)
                .HasMaxLength(3)
                .IsRequired();
            priceAmount.HasData(new { PriceProductId = 1, Value = 1m, Currency = Currency.EUR });
        });
        price.HasData(new { ProductId = 1 });
    });
    builder.HasData(new { Id = 1 });
});

@vyruz1986
Copy link
Author

Ah I see, I was still trying to seed the Product data with the actual type, instead of also using an anonymous there as well.
This fixed it for me, thanks!

@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Jan 18, 2024
@ajcvickers ajcvickers added the closed-no-further-action The issue is closed and no further action is planned. label Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

2 participants