efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Error when setting column attributes

Open r-work opened this issue 2 years ago • 3 comments

Not sure how to explain the issue, but I can demonstrate two scenarios where it works with one but not the other, so here it goes (or check the repro repo:

Scenario 1

If we create a table like the following (note that column names are PascalCase):

CREATE TABLE IF NOT EXISTS public."GenericTable1"
(
    "Id" bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
    "TypeId" bigint,
    "Content" text COLLATE pg_catalog."default",
    CONSTRAINT generic_table1_pkey PRIMARY KEY ("Id")
)

And have a bit of code like the following

    public class MyDbContext1 : DbContext
    {
        public DbSet<GenericTable1> T1 { get; set; }
        public DbSet<GenericTable1<GenericTableData1>> T2 { get; set; }
        public DbSet<GenericTable1<GenericTableData2>> T3 { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseNpgsql("Server=localhost;Port=5432;Database=testdb;User Id=postgres;Password=123456");

            base.OnConfiguring(optionsBuilder);
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<GenericTable1>().HasKey(x => x.Id);
            modelBuilder.Entity<GenericTable1>().HasDiscriminator(x => x.TypeId).HasValue(-1);

            modelBuilder.Entity<GenericTable1<GenericTableData1>>().HasDiscriminator(x => x.TypeId).HasValue(1);
            modelBuilder.Entity<GenericTable1<GenericTableData2>>().HasDiscriminator(x => x.TypeId).HasValue(2);

            modelBuilder.Entity<GenericTable1<GenericTableData1>>().Property(x => x.Content).HasColumnType("jsonb");
            modelBuilder.Entity<GenericTable1<GenericTableData2>>().Property(x => x.Content).HasColumnType("jsonb");

            base.OnModelCreating(modelBuilder);
        }
    }

    [Table("GenericTable1")]
    public class GenericTable1
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id { get; set; }

        public long TypeId { get; set; }
    }

    public class GenericTable1<T> : GenericTable1
    {
        public T Content { get; set; }
    }

And to run the above, works fine without any issues:

//this works
using (var context = new MyDbContext1())
{
     var data = context.T2.Where(x => x.Id > 0).ToList();
}

Scenario 2

Now if we create another table, pretty much the same as before (note that column names are snake_case):

CREATE TABLE IF NOT EXISTS public.generic_table2
(
    id bigint NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1 ),
    type_id bigint,
    content text COLLATE pg_catalog."default",
    CONSTRAINT generic_table2_pkey PRIMARY KEY (id)
)

And the code (note the 'Column' attributes:

    public class MyDbContext2 : DbContext
    {
        public DbSet<GenericTable2> T1 { get; set; }
        public DbSet<GenericTable2<GenericTableData1>> T2 { get; set; }
        public DbSet<GenericTable2<GenericTableData2>> T3 { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseNpgsql("Server=localhost;Port=5432;Database=testdb;User Id=postgres;Password=123456");

            base.OnConfiguring(optionsBuilder);
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<GenericTable2>().HasKey(x => x.Id);
            modelBuilder.Entity<GenericTable2>().HasDiscriminator(x => x.TypeId).HasValue(-1);

            modelBuilder.Entity<GenericTable2<GenericTableData1>>().HasDiscriminator(x => x.TypeId).HasValue(1);
            modelBuilder.Entity<GenericTable2<GenericTableData2>>().HasDiscriminator(x => x.TypeId).HasValue(2);

            modelBuilder.Entity<GenericTable2<GenericTableData1>>().Property(x => x.Content).HasColumnType("jsonb");
            modelBuilder.Entity<GenericTable2<GenericTableData2>>().Property(x => x.Content).HasColumnType("jsonb");

            base.OnModelCreating(modelBuilder);
        }
    }

    [Table("generic_table2")]
    public class GenericTable2
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Column("id")]
        public long Id { get; set; }

        [Column("type_id")]
        public long TypeId { get; set; }
    }

    public class GenericTable2<T> : GenericTable2
    {
        [Column("content")]
        public T Content { get; set; }
    }

And to run it:

//this doesn't work
using (var context = new MyDbContext2())
{
      var data = context.T2.Where(x => x.Id > 0).ToList();
}

It throws: 'GenericTable2<GenericTableData1>.Content' and 'GenericTable2<GenericTableData2>.Content' are both mapped to column 'content' in 'generic_table', but are configured to use differing provider types ('GenericTableData1' and 'GenericTableData2').

Stack trace

   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateCompatible(IProperty property, IProperty duplicateProperty, String columnName, StoreObjectIdentifier& storeObject, IDiagnosticsLogger`1 logger)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal.NpgsqlModelValidator.ValidateCompatible(IProperty property, IProperty duplicateProperty, String columnName, StoreObjectIdentifier& storeObject, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.ValidateSharedColumnsCompatibility(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 Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal.NpgsqlModelValidator.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.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(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 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.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 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.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 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.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 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.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 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.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 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(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_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_Model()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
   at EfTest.Program.Main(String[] args) in C:\Users\rebin\source\repos\EfTest\EfTest\Program.cs:line 16

Repro repo:

https://github.com/r-work/EfTest

Include provider and version information

EF Core version: 7.0.2 Database provider: Npgsql.EntityFrameworkCore.PostgreSQL v7.0.1 Target framework: .NET 7.0 Operating system: Windows 11 22H2 (22621.1105) IDE: Visual Studio 2022 17.5 Preview 3.0

This works if I downgrade to EntityFrameworkCore 6.0.13 and PostgreSQL 6.0.8

r-work avatar Jan 23 '23 13:01 r-work

Scenario 2 in OnModelCreating add: modelBuilder.Entity<GenericTable2>().ToTable("generic_table2"); modelBuilder.Entity<GenericTable2>().Property(p => p.Id).HasColumnName("id"); modelBuilder.Entity<GenericTable2>().Property(p => p.TypeId).HasColumnName("type_id");

SEmadH avatar Jan 24 '23 05:01 SEmadH

Scenario 2 in OnModelCreating add: modelBuilder.Entity<GenericTable2>().ToTable("generic_table2"); modelBuilder.Entity<GenericTable2>().Property(p => p.Id).HasColumnName("id"); modelBuilder.Entity<GenericTable2>().Property(p => p.TypeId).HasColumnName("type_id");

Done that, throws the same exception.

r-work avatar Jan 24 '23 08:01 r-work

Probably a duplicate of #29859.

/cc @AndriySvyryd @roji

ajcvickers avatar Jan 24 '23 18:01 ajcvickers