在EF中处理循环引用的清洁方式? [英] Clean way to deal with circular references in EF?
问题描述
客户
-----------
ClientId int not null(identity)
CurrentDemographicId int null(FK to ClientDemographic)
OtherClientFields varchar(100)null
ClientDemographic
------------------
ClientDemographicId int not null(identity)
ClientId int not null(FK to Client)
OtherClientDemographicFields varchar(100 )null
这个想法是,客户端(在EF中)将具有ClientDemographics列表和CurrentDemographic属性。
问题是当我设置对象结构并尝试保存它,我收到这个错误:
无法确定依赖操作的有效排序。由于外键约束,模型要求或存储生成值,可能存在依赖关系
此错误是有道理的。我的表格设置中有一个循环引用。它不知道首先插入哪个实体(因为它需要同时从两个表中的Id)。
所以,我把一个看起来像这样的解决方案:
//保存未更改的ClientDemograpic
ClientDemographic originalClientDemographic = client.CurrentClientDemographic;
//将合同合并到客户端对象
Mapper.Map(contract,client);
//如果这是一个新的客户端,然后添加为新列表。
if(client.ClientId == 0)
{
dataAccess.Add(client);
}
//恢复原始ClientDemographic,以便EF不会在循环引用上阻止
//。
ClientDemographic newClientDemographic = null;
if(client.CurrentClientDemographic!= originalClientDemographic)
{
newCurrentClientDemographic = client.CurrentClientDemographic;
client.CurrentClientDemographic = originalClientDemographic;
}
//将我们的更改保存到db。
dataAccess.SaveChanges();
//将更新更新到ClientDemographics并保存(如果需要)
if(newClientDemographic!= null)
{
client.CurrentClientDemographic = newCurrentClientDemographic;
dataAccess.SaveChanges();
}
但将参考更改为上一个值,保存,然后再次设置所以我可以再次保存感觉像一个黑客。
有没有更清洁的方法来处理EF中的循环引用?
我会说答案是:不是真的。处理循环引用的唯一干净的方法是重新设计并删除它。
在这种情况下 - 从域驱动设计的角度来看 - 我'd说客户端
是您的聚合的根, ClientDemographic
是一个值对象; ClientDemographics
由其他ClientDemographic字段的值定义。因此,您可以从 ClientDemographic
中删除 ClientId
,并且阻止该问题而不是治愈。
那就是说,如果你在这个结构上安定下来,不幸的是我不认为在EF中没有一个整洁的方法来处理这个问题,而不是。
编辑:要给客户端
多个 ClientDemographics
以及 CurrentClientDemographic
属性,你可以走另一种方式;从客户端
中删除 CurrentClientDemographicId
,并添加一个 IsCurrent
二进制字段至 ClientDemographic
。 EF然后为您提供一个 ClientDemographics
collection属性,您可以将自己添加到新的部分类中:
public partial class Client
{
public ClientDemographic CurrentDemogaphic
{
get {return this.ClientDemographics.First(cd => cd .IsPrimary); }
}
}
Say I have this table structure:
Client
-----------
ClientId int not null (identity)
CurrentDemographicId int null (FK to ClientDemographic)
OtherClientFields varchar(100) null
ClientDemographic
------------------
ClientDemographicId int not null (identity)
ClientId int not null (FK to Client)
OtherClientDemographicFields varchar(100) null
The idea is that Client (in EF) will have a ClientDemographics list and a CurrentDemographic property.
The problem is when I setup the object structure and try to save it, I get this error:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
This error makes sense. I have a circular reference in my table setup. It does not know which entity to insert first (because it needs the Id from both tables at the same time).
So, I hacked together a solution that looks like this:
// Save off the unchanged ClientDemograpic
ClientDemographic originalClientDemographic = client.CurrentClientDemographic;
// Merge the contract into the client object
Mapper.Map(contract, client);
// If this is a new client then add as new to the list.
if (client.ClientId == 0)
{
dataAccess.Add(client);
}
// Restore the original ClientDemographic so that EF will not choke
// on the circular reference.
ClientDemographic newClientDemographic = null;
if (client.CurrentClientDemographic != originalClientDemographic)
{
newCurrentClientDemographic = client.CurrentClientDemographic;
client.CurrentClientDemographic = originalClientDemographic;
}
// save our changes to the db.
dataAccess.SaveChanges();
// Restore updates to ClientDemographics and save (if needed)
if (newClientDemographic != null)
{
client.CurrentClientDemographic = newCurrentClientDemographic;
dataAccess.SaveChanges();
}
But changing the reference back to the previous value, saving, then setting it again so I can save again feels like a hack.
Is there a cleaner way to deal with circular references in EF?
I'd say the answer is: "not really". The only clean way to deal with the circular reference is to look again at the design and remove it.
In this case - approaching it from a Domain Driven Design perspective - I'd say that Client
is the root of your aggregate and ClientDemographic
is a value object; ClientDemographics
are defined by the values of their 'Other ClientDemographic fields'. You can therefore remove ClientId
from ClientDemographic
, and the problem is prevented instead of cured.
That said, if you're settled on this structure then unfortunately I don't think there's a neat way of handling it in EF, no.
Edit: To give Client
multiple ClientDemographics
as well as a CurrentClientDemographic
property, you can go the other way; remove CurrentClientDemographicId
from Client
, and add an IsCurrent
binary field to ClientDemographic
. The EF then gives you a ClientDemographics
collection property, and you can add the following yourself in a new, partial class:
public partial class Client
{
public ClientDemographic CurrentDemogaphic
{
get { return this.ClientDemographics.First(cd => cd.IsPrimary); }
}
}
这篇关于在EF中处理循环引用的清洁方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!