首页 > 解决方案 > 使用“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(EntityTypeBuilderC:\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.CallSiteVisitor2.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.CallSiteVisitor2.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.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](IInfrastructure1 访问器) 在 Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure1 accessor) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func1 个工厂)在 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() 值不能为空。(参数“属性”)

这两个代码应该做完全相同的事情,但第二个不起作用。我在这里想念什么?

标签: c#entity-framework-core

解决方案


您必须先应用配置Transition,然后再应用ItemState.

modelBuilder.ApplyConfiguration(new TransitionConfig());
modelBuilder.ApplyConfiguration(new ItemStateConfig());

否则,导航尚未定义并FindNavigation返回null,因此出现异常。

这就是为什么最好在配置关系的地方配置导航属性,使用Metadata关系构建器的属性获取关联的IMutableForeignKey,然后使用PrincipalToDependentDependentTiProncipal属性获取对应的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,从而避免这样的“排序”问题。


推荐阅读