c# - Double Relation One to Many (different Property same Class), Unable to determine the relationship represented by navigation, C#
问题描述
I want to establish double relation using List
between NxProperty
custom class and NxLog
custom 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; }
}
In the DbContext
daughter class I have:
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
}
}
But I get the Exception:
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>
I need to be sure that I have the right code.
I was trying with
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);
But I get...
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'.
How I must to establish the relation in the OnModelCreating(ModelBuilder modelBuilder)
method?
Can you share with me the code for modelBuilder.Entity<NxProperty>().HasMany(...)
?
Thanks in advance
解决方案
I had to look at like 9 times to understand your code. So first of all, having the same class represent multiple tables is just wrong in my honest opinion. If you want to handle them together create a base class for both and then create child classes. This is why you can't map them correctly, because the name is ambiguous. But I'm here to help.
- Step: Refactor your
NxProperty
class.
- Make NxProperty an abstract class.
- Create an empty class which inherits from this class called
NxRequestProperty
. - Create another empty class which inherits from this class called
NxResponseProperty
.
Example:
//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 {
}
- Step: Refactor your
NxLog
class. Use the concrete classes instead of theNxProperty
abstract class.
Example:
// 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; }
}
- Step: Refactor your
CustomDbContext
. YouNxProperty
class is now abstract and you should not use it as aDbSet
(imho), let's refactor it.
Example:
// 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
}
}
- (and final) Step: I'm not sure whether EF Core could map the relations now, but I believe in an always-declare source code is a better source code, so here is how to map it. Due to having composite keys, you need to have the foreign keys as all the composite keys.
Example:
// 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 });
EDIT:
Looking at my answer I realized that maybe refactoring NxProperty
to an interface is better (and calling it INxProperty
). Both would work, but I think the interface is better suited for this.
推荐阅读
- java - 为什么 java 的 orElse 块总是被执行?
- angular - 路由url在角度发生变化时如何刷新页面
- python - 使用 Python 重命名不同文件夹中的文件
- node.js - Nodejs请求功能仍未完成但代码继续下一步?
- sql - 具有不同类型数据的存储过程
- azure - 如何获取 Azure DevOps 管道手动取消回调
- javascript - Javascript:从 Range Slider 获取函数外的变量
- php - 如何使用 docker 和 kubernetes 在 laravel-echo-server 主机中使用 url 之类的子域而不是端口号?
- php - php echo语句执行失败
- php - 从索引到索引选择