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

TPT: Cannot create relationship between derived classes using base's relationship #22064

Closed
eraffel-MDSol opened this issue Aug 14, 2020 · 5 comments · Fixed by #22066
Closed
Labels
area-model-building closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@eraffel-MDSol
Copy link

I think the model will make it more obvious, but I'm having issues mapping the relationship between two derived classes, using the underlying relationship between the base classes.

Steps to reproduce

Try to create a migration using the following context and entities

    public class MyContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Parent>().ToTable("Parent");
            modelBuilder.Entity<ParentBase>().ToTable("ParentBase");

            modelBuilder.Entity<Child>().ToTable("Child");
            modelBuilder.Entity<ChildBase>().ToTable("ChildBase");

            modelBuilder.Entity<Child>()
                .HasOne(c => c.Parent)
                .WithMany(p => p.Children)
                .HasPrincipalKey(parent => parent.Id)
                .HasForeignKey(c => c.BaseParentId);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Server=localhost;Database=Test;Trusted_Connection=True;");
        }
    }

    public abstract class ParentBase
    {
        public Guid Id { get; set; }
    }

    public class Parent : ParentBase
    {
        public List<Child> Children { get; set; }
    }

    public abstract class ChildBase
    {
        public Guid Id { get; set; }
        public ParentBase BaseParent { get; set; }

        public Guid BaseParentId { get; set; }
    }

    public class Child : ChildBase
    {
        public Parent Parent { get; set; }
    }

When I try to create a migration, I get the following.

System.ArgumentNullException: Value cannot be null. (Parameter 'key')
   at System.Collections.Generic.Dictionary`2.FindEntry(TKey key)
   at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.SharedTableConvention.TryUniquifyForeignKeyNames(IConventionEntityType entityType, Dictionary`2 foreignKeys, StoreObjectIdentifier& storeObject, Int32 maxLength)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.SharedTableConvention.ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalizing(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalizing(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
   at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, 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.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, 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__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   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.<Exe

Further technical details

EF Core version: 5.0.0-rc.1.20414.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: NET Core 3.1
Operating system: Windows 10
IDE: Visual Studio 2019 16.7.1

AndriySvyryd added a commit that referenced this issue Aug 14, 2020
@AndriySvyryd AndriySvyryd added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Aug 14, 2020
@AndriySvyryd AndriySvyryd added this to the 5.0.0 milestone Aug 14, 2020
@ghost ghost closed this as completed in #22066 Aug 14, 2020
ghost pushed a commit that referenced this issue Aug 14, 2020
@ajcvickers ajcvickers modified the milestones: 5.0.0, 5.0.0-rc1 Aug 14, 2020
@eraffel-MDSol
Copy link
Author

It's not throwing an error anymore, but it's creating both a BaseParentId and BaseParentId1 column, and it's creating a FK from ChildBase to Parent, which also wouldn't be desired.

@AndriySvyryd
Copy link
Member

AndriySvyryd commented Aug 15, 2020

but it's creating both a BaseParentId and BaseParentId1 column

You need to configure the base relationship or remove BaseParent to avoid that

            modelBuilder.Entity<ChildBase>()
                .HasOne(c => c.BaseParent)
                .WithMany()
                .HasPrincipalKey(parent => parent.Id)
                .HasForeignKey(c => c.BaseParentId);

and it's creating a FK from ChildBase to Parent, which also wouldn't be desired.

You can remove it in the generated migration

@eraffel-MDSol
Copy link
Author

Yes, that worked. Thank you!

@ajcvickers ajcvickers modified the milestones: 5.0.0-rc1, 5.0.0 Nov 7, 2020
@eraffel-MDSol
Copy link
Author

eraffel-MDSol commented Nov 20, 2020

@AndriySvyryd I didn't notice this cause I wasn't logging to console, but this is generating the warning from this: #22175

The foreign key {'BaseParentId'} on the entity type 'Child' targeting 'Parent' cannot be represented in the database. Either the properties {'BaseParentId'} aren't mapped to table 'Child' or the principal properties {'Id'} aren't mapped to table 'Parent'. All foreign key properties must map to the table that the dependent type is mapped to and all principal properties must map to a single table that the principal type is mapped to.

but I can't tell if there are actually any issues, or if I should ignore the error message

for the record, the current context:

   public class MyContext : DbContext
    {
        public DbSet<Parent> Parents { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Parent>().ToTable("Parent");
            modelBuilder.Entity<ParentBase>().ToTable("ParentBase");

            modelBuilder.Entity<Child>().ToTable("Child");
            modelBuilder.Entity<ChildBase>().ToTable("ChildBase");

            modelBuilder.Entity<ChildBase>()
                .HasOne(c => c.BaseParent)
                .WithMany()
                .HasPrincipalKey(parent => parent.Id)
                .HasForeignKey(c => c.BaseParentId);

            modelBuilder.Entity<Child>()
                .HasOne(c => c.Parent)
                .WithMany(p => p.Children)
                .HasPrincipalKey(parent => parent.Id)
                .HasForeignKey(c => c.BaseParentId);
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseSqlServer("Server=localhost;Database=Test;Trusted_Connection=True;");
            optionsBuilder.LogTo(Console.WriteLine);
        }
    }

    public abstract class ParentBase
    {
        public Guid Id { get; set; }
    }

    public class Parent : ParentBase
    {
        public List<Child> Children { get; set; }
    }

    public abstract class ChildBase
    {
        public Guid Id { get; set; }
        public ParentBase BaseParent { get; set; }

        public Guid BaseParentId { get; set; }
    }

    public class Child : ChildBase
    {
        public Parent Parent { get; set; }
    }

@AndriySvyryd
Copy link
Member

@eraffel-MDSol You can ignore it, since you are configuring two relationships one of them will be created in the database

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-model-building closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants