EF Core 8 ComplexProperty 映射:无法将“ComplexType”转换为“IReadOnlyEntityType”类型

问题描述 投票:0回答:1

在映射任何聚合中的任何值对象时,我遇到以下异常:

 ---> System.InvalidCastException: Unable to cast object of type 'Microsoft.EntityFrameworkCore.Metadata.Internal.ComplexType' to type 'Microsoft.EntityFrameworkCore.Metadata.IReadOnlyEntityType'.
   at Microsoft.EntityFrameworkCore.Metadata.Internal.ComplexType.<>c.<get_ConstructorBinding>b__42_0(ComplexType complexType)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Action`1 valueFactory)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.ComplexType.get_ConstructorBinding()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.<ValidateFieldMapping>g__Validate|24_0(ITypeBase typeBase)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.<ValidateFieldMapping>g__Validate|24_0(ITypeBase typeBase)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateFieldMapping(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 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.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.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(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.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.Set[TEntity]()
   at Venture.Server.Common.Persistence.EntityEvents.PublishedEntityEventReadRepository.GetConsumedAsync(CancellationToken cancellationToken) in C:\Users\N.Wolf\source\repos\venture-erp\src\Venture.Server.Common\Persistence\EntityEvents\PublishedEntityEventReadRepository.cs:line 25
   at Venture.Server.Common.Persistence.EntityEvents.Jobs.EntityEventCleanupJob.Execute(IJobExecutionContext context) in C:\Users\N.Wolf\source\repos\venture-erp\src\Venture.Server.Common\Persistence\EntityEvents\Jobs\EntityEventCleanupJob.cs:line 18
   at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)

“部门”聚合及其配置:

public class Department
{
    public Guid AggregateId { get; protected set; }
    public DateTime Created { get; protected set; }
    public DateTime LastChanged { get; protected set; }
    public Guid EditEmployeeId { get; protected set; }
    public required string Name { get; init; }
    public string? Comment { get; init; }
    public required Address Address { get; init; }

    public Guid DivisionId { get; init; }
    public required Division Division { get; init; }

    public List<Role> EmployeeRoles { get; init; } = [];
    public List<EmployeeDepartmentAssignment> EmployeeAssignments { get; init; } = [];
    public List<DepartmentPosition> Positions { get; init; } = [];

    protected override void ApplyEvent(DepartmentEvent @event)
    {
        throw new NotImplementedException();
    }
}
public record Address
{
    public required string Street { get; init; }
    public required string HouseNumber { get; init; }
    public required string City { get; init; }
    public required string PostalCode { get; init; }
    public required string State { get; init; }
    public required string Country { get; init; }
}
        builder.ToTable("access_management_departments");
        builder.HasKey(a => a.AggregateId);
        builder.Property(a => a.AggregateId).HasColumnName("id").IsRequired();
        builder.Property(a => a.Created).HasColumnName("created").IsRequired();
        builder.Property(a => a.LastChanged).HasColumnName("last_changed").IsRequired();
        builder.Property(a => a.EditEmployeeId).HasColumnName("edit_employee_id").IsRequired();
        builder.Property(d => d.Name).HasColumnName("name").HasMaxLength(70).IsRequired();
        builder.Property(d => d.Comment).HasColumnName("comment").HasMaxLength(200);
        builder.ComplexProperty(d => d.Address, p =>
        {
            p.Property(a => a.Street).HasColumnName("address_street").IsRequired();
            p.Property(a => a.HouseNumber).HasColumnName("address_house_number").IsRequired();
            p.Property(a => a.City).HasColumnName("address_city").IsRequired();
            p.Property(a => a.PostalCode).HasColumnName("address_postal_code").IsRequired();
            p.Property(a => a.State).HasColumnName("address_state").IsRequired();
            p.Property(a => a.Country).HasColumnName("address_country").IsRequired();
            p.IsRequired();
        });
        builder.Property(d => d.DivisionId).HasColumnName("division_id").IsRequired();

        builder.Ignore(d => d.Positions);
        builder.Ignore(d => d.EmployeeAssignments);
        builder.Ignore(d => d.EmployeeRoles);
        builder.Ignore(d => d.Division);

        //builder.HasMany(d => d.Positions).WithOne().HasForeignKey(d => d.DepartmentId).OnDelete(DeleteBehavior.Cascade);
        //builder.HasMany(d => d.EmployeeAssignments).WithOne(e => e.Department).HasForeignKey(a => a.DepartmentId).OnDelete(DeleteBehavior.Cascade);
        //builder.HasMany(d => d.EmployeeRoles).WithMany().UsingEntity<DepartmentRole>();
        //builder.HasOne(d => d.Division).WithMany().HasForeignKey(d => d.DivisionId);

我尝试了很多方法,例如删除

required
关键字或使用类而不是地址记录等。根据我的经验,我在这里所做的任何事情都不会导致配置失败。但是,如果我忽略地址属性并注释掉
ComplexProperty
映射,则不会出现问题。我发现的最接近的帖子是 this 问题,但这似乎是 EF 核心积压工作的一部分。

我也尝试过根据堆栈跟踪来反编译和调试负责此操作的 EF 核心代码,但我担心我的无知让我无法得出任何结论。

我的问题是:我的尝试中是否存在任何明显的错误,或者这看起来像是 EF 核心错误吗?

.net entity-framework-core
1个回答
0
投票

就像平常一样,这是一些愚蠢的事情。

我不知道它到底是什么,但使用以下扩展方法来注册来自多个程序集的

IEntityTypeConfiguration<TEntity>

    private static DbContextOptionsBuilder AddServerModel(this DbContextOptionsBuilder options, IEnumerable<Assembly> assemblies)
    {
        var model = new ModelBuilder();
        foreach (var assembly in assemblies)
            model.ApplyConfigurationsFromAssembly(assembly);

        return options.UseModel(model.FinalizeModel());
    }

诚然不是很漂亮,但最重要的是有缺陷。

ModelBuilder
有一些 EF Core 内部构造函数,其中包含一些配置参数,用于配置我假设的模型构建过程。好吧,自从我实例化了自己的配置以来,这些配置现在已被跳过。

我现在已将这种方法更改为以下代码(暂时拧紧新命名):

internal abstract class DbContextRegistrationMetadata
{
    internal static DbContextRegistrationMetadata<TDbContext> Create<TDbContext>()
        where TDbContext : DbContext
    {
        return new DbContextRegistrationMetadata<TDbContext>();
    }
}

public sealed class DbContextRegistrationMetadata<TDbContext>
    where TDbContext : DbContext
{
    internal DbContextRegistrationMetadata()
    {
    }

    private Assembly[]? _entityConfigurationAssemblies;

    public DbContextRegistrationMetadata<TDbContext> SearchInAssembliesForEntityConfigurations(params Assembly[] entityConfigurationAssemblies)
    {
        _entityConfigurationAssemblies = entityConfigurationAssemblies;
        return this;
    }

    internal Assembly[]? GetEntityConfigurationAssemblies() => _entityConfigurationAssemblies;
}

public static class DbContextRegistrationMetadataExtensions
{
    public static IServiceCollection AddDbContextRegistrationMetadata<TDbContext>(this IServiceCollection services, Action<DbContextRegistrationMetadata<TDbContext>> metadataAction)
        where TDbContext : DbContext
    {
        var metadata = DbContextRegistrationMetadata.Create<TDbContext>();
        metadataAction.Invoke(metadata);

        return services.AddSingleton(sp => metadata);
    }
}

现在

DbContext
会像
DbContextOptions
一样收到此信息:

public class CommonDbContext(DbContextOptions<CommonDbContext> options, DbContextRegistrationMetadata<CommonDbContext> _registrationMetadata) : DbContext(options)
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        var entityConfigurationAssemblies = _registrationMetadata.GetEntityConfigurationAssemblies();
        if (entityConfigurationAssemblies == null)
            return;

        foreach (var assembly in entityConfigurationAssemblies)
            modelBuilder.ApplyConfigurationsFromAssembly(assembly);
    }
}

这允许我配置要扫描的多个程序集,而无需将包含

DbContext
实现的程序集引用到包含实体配置的程序集。

我真傻,真傻

© www.soinside.com 2019 - 2024. All rights reserved.