Double Relation One to Many(different属性sameClass),无法确定导航所代表的关系,C#

Double Relation One to Many (different Property same Class), Unable to determine the relationship represented by navigation, C#

我想使用 ListNxProperty 自定义 class 和 NxLog 自定义 class 之间建立双重关系。

public class NxLog
{
    public string NxLogId { get; set; }
    public string NxWorkspaceId { get; set; }
    public string NxConversationId { get; set; }
    public NxConversation NxConversation { get; set; }
    public List<NxOutputText> NxOutputTexts { get; set; }
    public List<NxProperty> RequestProperties { get; set; }     //1st Relation
    public List<NxProperty> ResponseProperties { get; set; }    //2nd Relation
}

public class NxProperty
{
    public string NxPropertyId { get; set; }
    public string NxWorkspaceId { get; set; }
    public string NxConversationId { get; set; }
    public string NxLogId { get; set; }
    public NxLog NxLog { get; set; }
    public JToken Value { get; set; }
}

DbContext女儿class我有:

class CustomDbContext:DbContext
{
    DbSet<NxWorkspace> Workspaces { get; set; }
    DbSet<NxConversation> Conversations { get; set; }
    DbSet<NxLog> Logs { get; set; }
    DbSet<NxOutputText> OutputTexts { get; set; }
    DbSet<NxProperty> RequestProperties { get; set; }
    DbSet<NxProperty> ResponseProperties { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<NxWorkspace>().HasKey(w => w.NxWorkspaceId);
        modelBuilder.Entity<NxConversation>().HasKey(c => new { c.NxWorkspaceId, c.NxConversationId });
        modelBuilder.Entity<NxLog>().HasKey(l => new { l.NxWorkspaceId, l.NxConversationId, l.NxLogId });
        modelBuilder.Entity<NxOutputText>().HasKey(o => new { o.NxWorkspaceId, o.NxConversationId, o.NxLogId, o.NxOutputTextId });
        modelBuilder.Entity<NxProperty>().HasKey(p => new { p.NxWorkspaceId, p.NxConversationId, p.NxLogId, p.NxPropertyId });
        
        modelBuilder.Entity<NxProperty>().Property(l => l.Value)
            .HasConversion<string>(
            o => JsonConvert.SerializeObject(o),
            d => JsonConvert.DeserializeObject<JToken>(d)
            );
            
        //Other steps
    }
}

但我得到了异常:

System.InvalidOperationException: Unable to determine the relationship represented by navigation 'NxLog.RequestProperties' of type 'List<NxProperty>'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidatePropertyMapping(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 Microsoft.EntityFrameworkCore.SqlServer.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IModel model)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IModel model)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IModel model)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
   at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   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)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, 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 singletonCallSite, 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__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 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Unable to determine the relationship represented by navigation 'NxLog.RequestProperties' of type 'List<NxProperty>'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
PM>

我需要确保我有正确的代码。

我正在尝试

modelBuilder.Entity<NxLog>()
    .HasMany(l => l.RequestProperties)
    .WithOne(p => p.NxLog).HasForeignKey(p => p.NxLogId);

modelBuilder.Entity<NxLog>()
    .HasMany(l => l.ResponseProperties)
    .WithOne(p => p.NxLog).HasForeignKey(p => p.NxLogId);

但我明白了...

Cannot create a relationship between 'NxLog.ResponseProperties' and 'NxProperty.NxLog' 
because a relationship already exists between 'NxLog.RequestProperties' and 'NxProperty.NxLog'. 
Navigation properties can only participate in a single relationship.
If you want to override an existing relationship call 'Ignore' on the navigation 
'NxProperty.NxLog' first in 'OnModelCreating'.

我必须如何在OnModelCreating(ModelBuilder modelBuilder)方法中建立关系?

你能与我分享 modelBuilder.Entity<NxProperty>().HasMany(...) 的代码吗?

提前致谢

我看了 9 遍才看懂你的代码。所以首先,在我看来,让相同的 class 代表多个表是错误的。如果你想一起处理它们,请为两者创建一个基础 class,然后创建子 classes。这就是为什么您无法正确映射它们的原因,因为名称不明确。但我是来帮忙的。

  1. 步骤:重构您的 NxProperty class.
  • 将 NxProperty 设为抽象 class。
  • 创建一个空的 class,它继承自这个名为 NxRequestProperty 的 class。
  • 创建另一个空的 class 继承自此 class,称为 NxResponseProperty

示例:

//NgProperty.cs
public abstract class NxProperty
{
    public string NxPropertyId { get; set; }
    public string NxWorkspaceId { get; set; }
    public string NxConversationId { get; set; }
    public string NxLogId { get; set; }
    public NxLog NxLog { get; set; }
    public JToken Value { get; set; }
}

// NxRequestProperty.cs
// using sealed is just my preference so other
// developers know not to inherit from it without
// asking questions, you can exclude it if you wish.
public sealed class NxRequestProperty : NxProperty {

}

// NxResponseProperty.cs
public sealed class NxResponseProperty : NxProperty {

}
  1. 步骤:重构您的 NxLog class。使用具体的 classes 而不是 NxProperty 抽象的 class.

示例:

// NxLog.cs
public sealed class NxLog
{
    public string NxLogId { get; set; }
    public string NxWorkspaceId { get; set; }
    public string NxConversationId { get; set; }

    public NxConversation NxConversation { get; set; }

    public List<NxOutputText> NxOutputTexts { get; set; }
    public List<NxRequestProperty> RequestProperties { get; set; }
    public List<NxResponseProperty> ResponseProperties { get; set; }
}
  1. 步骤:重构您的 CustomDbContext。您 NxProperty class 现在是抽象的,您不应该将其用作 DbSet(恕我直言),让我们重构它。

示例:

// CustomDbContext.cs
public sealed class CustomDbContext : DbContext
{
    DbSet<NxWorkspace> Workspaces { get; set; }
    DbSet<NxConversation> Conversations { get; set; }
    DbSet<NxLog> Logs { get; set; }
    DbSet<NxOutputText> OutputTexts { get; set; }
    // Refactor starts here!
    DbSet<NxRequestProperty> RequestProperties { get; set; }
    DbSet<NxResponseProperty> ResponseProperties { get; set; }
    // Refactor ends here!    

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<NxWorkspace>().HasKey(w => w.NxWorkspaceId);
        modelBuilder.Entity<NxConversation>().HasKey(c => new { c.NxWorkspaceId, c.NxConversationId });
        modelBuilder.Entity<NxLog>().HasKey(l => new { l.NxWorkspaceId, l.NxConversationId, l.NxLogId });
        modelBuilder.Entity<NxOutputText>().HasKey(o => new { o.NxWorkspaceId, o.NxConversationId, o.NxLogId, o.NxOutputTextId });

        // Refactor starts here!
        modelBuilder.Entity<NxRequestProperty>().HasKey(p => new { p.NxWorkspaceId, p.NxConversationId, p.NxLogId, p.NxPropertyId });
        modelBuilder.Entity<NxResponseProperty>().HasKey(p => new { p.NxWorkspaceId, p.NxConversationId, p.NxLogId, p.NxPropertyId });
        
        modelBuilder.Entity<NxRequestProperty>().Property(l => l.Value)
            .HasConversion<string>(
            o => JsonConvert.SerializeObject(o),
            d => JsonConvert.DeserializeObject<JToken>(d)
        );
          
        modelBuilder.Entity<NxResponseProperty>().Property(l => l.Value)
            .HasConversion<string>(
            o => JsonConvert.SerializeObject(o),
            d => JsonConvert.DeserializeObject<JToken>(d)
        );
        // Refactor ends here!
        //Other steps
    }
}
  1. (和最后)步骤:我不确定 EF Core 现在是否可以映射关系,但我相信始终声明的源代码是更好的源代码,所以这里是映射它的方法。由于有组合键,你需要有外键作为所有组合键。

示例:

// CustomDbContext.cs -> inside the OnModelCreating method
modelBuilder.Entity<NxRequestProperty>
    .HasOne(p => p.NxLog)
    .WithMany(l => l.RequestProperties)
    .HasForeignKey(l => new { l.NxWorkspaceId, l.NxConversationId, l.NxLogId });

modelBuilder.Entity<NxResponseProperty>
    .HasOne(p => p.NxLog)
    .WithMany(l => l.ResponseProperties)
    .HasForeignKey(l => new { l.NxWorkspaceId, l.NxConversationId, l.NxLogId });

编辑:

看着我的回答,我意识到也许将 NxProperty 重构为接口更好(并将其称为 INxProperty)。两者都可以,但我认为界面更适合这个。