Skip to content

RelationalPropertyExtensions.SetColumnName() breaks OwnsOne() model creation in RC1 #22608

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
cailenmusselman opened this issue Sep 18, 2020 · 1 comment

Comments

@cailenmusselman
Copy link

Setting the column name via SetColumnName() in OnModelCreating() seems to break OwnsOne() entities. This used to work as expected in EF Core 3 (and v5-preview4 I'm pretty sure) but in v5-RC1 the following exception occurs:

The keys {'Id'} on 'MainEntity' and {'MainEntityId'} on 'FirstOwnedEntity' are both mapped to 'MainEntities.PK_MainEntities' but with different columns ({'Id'} and {'MainEntityId'}).'

When removing the property.SetColumnName(property.GetColumnName()) the model/schema is created successfully. Oddly enough, when inspecting the database schema there isn't even a column/key named "MainEntityId".

Steps to reproduce

To reproduce, run the OwnsOnePropertyNamingConventionRC1 project attached (uses RC1 Nuget packages for EntityFrameworkCore+Sqlite+Npgsql):
OwnsOnePropertyNamingConventionRC1.zip

To compare to old behavior, run the OwnsOnePropertyNamingConvention project attached (uses EF Core 3 packages).
OwnsOnePropertyNamingConvention.zip

Verbose output of dotnet ef migrations add InitialCreate is as follows:

System.InvalidOperationException: The keys {'Id'} on 'MainEntity' and {'MainEntityId'} on 'FirstOwnedEntity' are both mapped to 'MainEntities.PK_MainEntities' but with different columns ({'Id'} and {'MainEntityId'}).
   at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationalKeyExtensions.AreCompatible(IKey key, IKey duplicateKey, StoreObjectIdentifier& storeObject, Boolean shouldThrow)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateCompatible(IKey key, IKey duplicateKey, String keyName, StoreObjectIdentifier& storeObject, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateSharedKeysCompatibility(IReadOnlyList`1 mappedTypes, StoreObjectIdentifier& storeObject, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateSharedTableCompatibility(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Sqlite.Internal.SqliteModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IModel model)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IModel model)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IModel model)
   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.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The keys {'Id'} on 'MainEntity' and {'MainEntityId'} on 'FirstOwnedEntity' are both mapped to 'MainEntities.PK_MainEntities' but with different columns ({'Id'} and {'MainEntityId'}).

Further technical details

EF Core version: Version 5.0.0-rc.1.20451.13
Database provider: Microsoft.EntityFrameworkCore.Sqlite + Npgql.EntityFrameworkCore.PostgreSQL
Target framework: .NET Core 3.0
Operating system: Windows 10
IDE: Visual Studio 2019 16.6.5

@ajcvickers
Copy link
Contributor

Note for team: I am able to reproduce this on the latest daily and it is a regression from 3.1. Simple repo below. The issue is that the result of calling GetColumnName while the model is being built has changed. In 3.1, it returns Id and everything works correctly. In 5.0 it returns MainEntityId and blows up in model validation. Trying to set MainEntityId explicitly in 3.1 results in the same exception.

@roji Does your naming convention try to get the current names of columns before the model is built?

public class Program
{
    private static void Main()
    {
        using var context = new Context();

        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();
    }
}

public class Context : DbContext
{
    public DbSet<MainEntity> MainEntities { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
        => optionsBuilder.UseSqlite("Datasource=test.db");

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<MainEntity>(b =>
        {
            b.HasKey(b => b.Id);
            b.OwnsOne(b => b.FirstOwnedEntity).WithOwner();
            b.OwnsOne(b => b.SecondOwnedEntity).WithOwner();
        });

        var propertyOne = builder.Model
            .FindEntityType(typeof(FirstOwnedEntity))
            .FindProperty("MainEntityId");

        Console.WriteLine($"Reported column name: {propertyOne.GetColumnName()}");
        propertyOne.SetColumnName(propertyOne.GetColumnName());
        
        var propertyTwo = builder.Model
            .FindEntityType(typeof(SecondOwnedEntity))
            .FindProperty("MainEntityId");

        Console.WriteLine($"Reported column name: {propertyTwo.GetColumnName()}");
        propertyTwo.SetColumnName(propertyTwo.GetColumnName());
    }
}

public class MainEntity
{
    public int Id { get; set; }
    public FirstOwnedEntity FirstOwnedEntity { get; set; }
    public SecondOwnedEntity SecondOwnedEntity { get; set; }
}

public class FirstOwnedEntity
{
    public int SomeValue { get; set; }
    public int PropertyNamingConventionIssue { get; set; }
}

public class SecondOwnedEntity
{
    public int AnotherValue { get; set; }
    public int PropertyNamingConventionIssue { get; set; }
}

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

3 participants