c# - 使用“ApplyConfiguration”时,EF Core 迁移不起作用
问题描述
我的模型中有以下两个类:
public class MyState
{
private HashSet<MyTransition> _myTransitions;
private HashSet<MyTransition> _usedBy;
public MyState()
{
_myTransitions = new HashSet<MyTransition>();
_usedBy = new HashSet<MyTransition>();
}
public Guid Id { get; set; }
public IEnumerable<MyTransition> MyTransitions => _myTransitions?.ToList();
public IEnumerable<MyTransition> UsedBy => _usedBy?.ToList();
}
public class MyTransition
{
public Guid Id { get; set; }
public Guid ParentId { get; set; }
public MyState Parent { get; set; }
public Guid StateId { get; set; }
public MyState State { get; set; }
}
当我DbContext
像这样创建时,add-migration
命令有效:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MyTransition>()
.HasOne(x => x.Parent)
.WithMany(x => x.MyTransitions)
.HasForeignKey(x => x.ParentId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<MyTransition>()
.HasOne(x => x.State)
.WithMany(x => x.UsedBy)
.HasForeignKey(x => x.StateId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<MyState>().Metadata
.FindNavigation(nameof(MyState.UsedBy))
.SetPropertyAccessMode(PropertyAccessMode.Field);
modelBuilder.Entity<MyState>().Metadata
.FindNavigation(nameof(MyState.MyTransitions))
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
但是当我尝试将配置移动到单独的类时,像这样,add-migration
命令失败:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new ItemStateConfig());
modelBuilder.ApplyConfiguration(new TransitionConfig());
}
}
public class TransitionConfig : IEntityTypeConfiguration<MyTransition>
{
public void Configure(EntityTypeBuilder<MyTransition> entity)
{
entity.HasOne(x => x.Parent)
.WithMany(x => x.MyTransitions)
.HasForeignKey(x => x.ParentId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(x => x.State)
.WithMany(x => x.UsedBy)
.HasForeignKey(x => x.StateId)
.OnDelete(DeleteBehavior.Restrict);
}
}
public class ItemStateConfig : IEntityTypeConfiguration<MyState>
{
public void Configure(EntityTypeBuilder<MyState> entity)
{
entity.Metadata
.FindNavigation(nameof(MyState.UsedBy))
.SetPropertyAccessMode(PropertyAccessMode.Field);
entity.Metadata
.FindNavigation(nameof(MyState.MyTransitions))
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
抛出的异常如下:
System.ArgumentNullException:值不能为空。
1 propertyAccessMode) at WebApplication5.Data.ItemStateConfig.Configure(EntityTypeBuilder
C:\Users\rbasn\中 Microsoft.EntityFrameworkCore.MutablePropertyBaseExtensions.SetPropertyAccessMode(IMutablePropertyBase property, Nullable 1 entity) 中 Microsoft.EntityFrameworkCore.Utilities.Check.NotNull[T](T value, String parameterName) 中的(参数“属性”)source\repos\WebApplication5\WebApplication5\Data\ClassS.cs:第 29 行,位于 Microsoft.EntityFrameworkCore.ModelBuilder.ApplyConfiguration[TEntity](IEntityTypeConfiguration1 configuration) at WebApplication5.Data.ApplicationDbContext.OnModelCreating(ModelBuilder modelBuilder) in C:\Users\rbasn\source\repos\WebApplication5\WebApplication5\Data\ApplicationDbContext.cs:line 21 at Microsoft.EntityFrameworkCore.Infrastructure.ModelCustomizer.Customize(ModelBuilder modelBuilder, DbContext context) at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder) at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder) 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) 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceSCallSiteton single , RuntimeResolverContext 上下文)在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.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) 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceSCallSiteton single , RuntimeResolverContext 上下文) 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.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 访问器) 在 Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure1 accessor) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func
1 个工厂)在 Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType) 在 Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType) 在 Microsoft.EntityFrameworkCore.Design.OperationExecutor。 AddMigrationImpl(String name, String outputDir, String contextType) 在 Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0() 在 Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1。 Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action) 处的 b__0() 值不能为空。(参数“属性”)
这两个代码应该做完全相同的事情,但第二个不起作用。我在这里想念什么?
解决方案
您必须先应用配置Transition
,然后再应用ItemState
.
modelBuilder.ApplyConfiguration(new TransitionConfig());
modelBuilder.ApplyConfiguration(new ItemStateConfig());
否则,导航尚未定义并FindNavigation
返回null
,因此出现异常。
这就是为什么最好在配置关系的地方配置导航属性,使用Metadata
关系构建器的属性获取关联的IMutableForeignKey
,然后使用PrincipalToDependent
或DependentTiProncipal
属性获取对应的IMutableNavigation
。
例如
public class TransitionConfig : IEntityTypeConfiguration<MyTransition>
{
public void Configure(EntityTypeBuilder<MyTransition> entity)
{
entity.HasOne(x => x.Parent)
.WithMany(x => x.MyTransitions)
.HasForeignKey(x => x.ParentId)
.OnDelete(DeleteBehavior.Restrict)
.Metadata.PrincipalToDependent // x.MyTransitions
.SetPropertyAccessMode(PropertyAccessMode.Field);
entity.HasOne(x => x.State)
.WithMany(x => x.UsedBy)
.HasForeignKey(x => x.StateId)
.OnDelete(DeleteBehavior.Restrict)
.Metadata.PrincipalToDependent // x.UsedBy
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
一般来说,关系在IEntityTypeConfiguration
概念上不太适合,因为关系不属于任何涉及的实体类型。例如,可以在其他实体配置中配置相同的关系:
public class ItemStateConfig : IEntityTypeConfiguration<MyState>
{
public void Configure(EntityTypeBuilder<MyTransition> entity)
{
entity.HasMany(x => x.MyTransitions)
.WithOne(x => x.Parent)
.HasForeignKey(x => x.ParentId)
.OnDelete(DeleteBehavior.Restrict)
.Metadata.PrincipalToDependent // x.MyTransitions
.SetPropertyAccessMode(PropertyAccessMode.Field);
entity.HasMany(x => x.UsedBy)
.WithOne(x => x.State)
.HasForeignKey(x => x.StateId)
.OnDelete(DeleteBehavior.Restrict)
.Metadata.PrincipalToDependent // x.UsedBy
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
因此,我更喜欢直接配置关系OnModelCreating
而不是单独配置关系IEntityTypeConfiguration
,从而避免这样的“排序”问题。
推荐阅读
- null - Avro 枚举字段
- python - 通过 pip 在 python 3.5 gpu 中安装 tensorflow 后的错误
- reactjs - React.js,从api中拉取数据然后循环显示
- python - Python csv writer 在单行中组合两列
- javascript - 从 sql 检索数据时,我想使用 where 条件作为变量
- flask - Flask-User,如何使用 uuid 而不是 int 作为用户 id
- php - 我需要在每次 PHP 调试会话后重新启动 NetBeans,但不知道为什么?
- r - 从 Shiny,Plotly - R 中提取所有点击事件图
- hive - ORC 文件格式
- android - 如何根据每个子 autogeneratedID 更新多个 firebase 子值