在映射任何聚合中的任何值对象时,我遇到以下异常:
---> 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 核心错误吗?
我不知道它到底是什么,但使用以下扩展方法来注册来自多个程序集的
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
实现的程序集引用到包含实体配置的程序集。
我真傻,真傻