Mas*_*oud 29 c# entity-framework ef-code-first graphdiff
在使用EF更新期间出现以下错误:
操作失败:无法更改关系,因为一个或多个外键属性不可为空.当对关系进行更改时,相关的外键属性将设置为空值.如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象.
有没有一般的方法来查找哪些外键属性导致上述错误?
[更新]
对于以下代码导致上述错误的一种情况(我在断开连接的环境中工作,所以我曾经graphdiff更新过我的对象图),当它想要运行时_uow.Commit();:
public void CopyTechnicalInfos(int sourceOrderItemId, List<int> targetOrderItemIds)
{
_uow = new MyDbContext();
var sourceOrderItem = _uow.OrderItems
.Include(x => x.NominalBoms)
.Include("NominalRoutings.NominalSizeTests")
.AsNoTracking()
.FirstOrDefault(x => x.Id == sourceOrderItemId);
var criteria = PredicateBuilder.False<OrderItem>();
foreach (var targetOrderItemId in orderItemIds)
{
int id = targetOrderItemId;
criteria = criteria.OR(x => x.Id == id);
}
var targetOrderItems = _uow.OrderItems
.AsNoTracking()
.AsExpandable()
.Where(criteria)
.ToList();
foreach (var targetOrderItem in targetOrderItems)
{
//delete old datas and insert new datas
targetOrderItem.NominalBoms = sourceOrderItem.NominalBoms;
targetOrderItem.NominalBoms.ForEach(x => x.Id = 0);
targetOrderItem.NominalRoutings = sourceOrderItem.NominalRoutings;
targetOrderItem.NominalRoutings.ForEach(x => x.Id = 0);
targetOrderItem.NominalRoutings
.ForEach(x => x.NominalTests.ForEach(y => y.Id = 0));
targetOrderItem.NominalRoutings
.ForEach(x => x.NominalSizeTests.ForEach(y => y.Id = 0));
_uow.OrderItems.UpdateGraph(targetOrderItem,
x => x.OwnedCollection(y => y.NominalBoms)
.OwnedCollection(y => y.NominalRoutings,
with => with
.OwnedCollection(t => t.NominalTests)));
}
_uow.Commit();
}
Run Code Online (Sandbox Code Playgroud)
Ger*_*old 62
在Entity Framework中,您可以使用外键关联.也就是说,另一个对象的外键表示为一对两个属性:原始外键属性(例如NominalRouting.OrderItemId)和对象引用(NominalRouting.OrderItem).
这意味着您可以设置原始值或对象引用以建立外键关联.如果您设置其中一个,EF会尽可能保持另一个同步.不幸的是,这也可能引起原始外键值与其伴随参考之间的冲突.
很难说出你的情况究竟发生了什么.不过,我不知道你从一个父母的另一个"复制"对象的方法是......不理想.首先,更改主键值绝不是一个好主意.通过将它们设置为0您使对象看起来像新的,但它们不是.其次,您将多个相同的子对象分配给其他父对象.我认为,作为一个结果,你最终得到大量具有外键对象的值,而不是引用.
我说"复制",因为那是你看似试图实现的.如果是这样,您应该正确克隆对象和Add它们targetOrderItem.与此同时,我想知道为什么你(显然)克隆所有这些对象.看起来多对多关联在这里更合适.但这是一个不同的主题.
现在你的实际问题是:如何找到冲突的关联?
这非常非常困难.它需要代码来搜索概念模型并找到涉及外键关联的属性.然后你必须找到他们的价值观并发现不匹配.与确定可能的冲突何时是真正的冲突相比,这很难,但是微不足道.让我通过两个例子来澄清这一点.这里,类OrderItem具有由属性Order和组成的必需外键关联OrderId.
var item = new OrderItem { OrderId = 1, ... };
db.OrderItems.Add(item);
db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
所以有一个OrderId分配的项目和Order= null,EF很高兴.
var item = db.OrderItems.Include(x => x.Order).Find(10);
// returns an OrderItem with OrderId = 1
item.Order = null;
db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
同样,一个具有OrderId赋值和Order= null的项,但EF抛出异常"关系无法更改...".
(还有更多可能的冲突情况)
因此,仅仅OrderId/Order成对地查找不匹配的值是不够的,您还必须检查实体状态并确切地知道不允许不匹配的状态组合.我的建议:忘记它,修复你的代码.
虽然有一个肮脏的伎俩.当EF尝试匹配外键值和引用,某处嵌套树深处if的IT收集我们谈论到的一个成员变量冲突ObjectStateManager,命名_entriesWithConceptualNulls.通过做一些反思可以获得它的价值:
#if DEBUG
db.ChangeTracker.DetectChanges(); // Force EF to match associations.
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
var objectStateManager = objectContext.ObjectStateManager;
var fieldInfo = objectStateManager.GetType().GetField("_entriesWithConceptualNulls", BindingFlags.Instance | BindingFlags.NonPublic);
var conceptualNulls = fieldInfo.GetValue(objectStateManager);
#endif
Run Code Online (Sandbox Code Playgroud)
conceptualNulls是HashSet<EntityEntry>,EntityEntry是一个内部类,所以只能检查在调试器中收集得到相互矛盾的实体的想法.仅用于诊断目的!!!
| 归档时间: |
|
| 查看次数: |
16586 次 |
| 最近记录: |