我有一个名为Cost
的实体,其必需属性为CostType
Cost
类有GetNew()
方法设置所有费用的默认值:
public static GetNew()
{
Cost cost = new Cost ();
foo.CostType = Lists.CostTypes.FirstOrDefault();
// Other Default Values
return foo;
}
Lists.CostTypes
是一个静态列表,在启动时从EF中提取并在ComboBoxes中使用
首先在GetNew()
方法中设置CostType后,我在设置CostType时遇到问题。
例如,以下代码读取excel文件,并根据Excel文件中的列设置默认类型,如果找不到匹配项则设置为null
Cost cost = Cost.GetNew();
cost.CostType = Lists.CostTypes.FirstOrDefault(t => t.Name == row[0].ToString());
我的问题是,在保存操作期间,我收到以下错误:
操作失败:无法更改关系,因为 一个或多个外键属性是不可为空的。当一个 改变了关系,相关的外键属性是 设置为空值。如果外键不支持空值, 必须定义新的关系,外键属性必须是 分配了另一个非空值,或者不相关的对象必须是 删除。
我的添加操作如下所示:
public static void AddObject(EntityObject obj, string entitySetName)
{
context.AddObject(entitySetName, obj);
context.SaveChanges();
}
GetNew()
中设置默认值的代码行,我会收到一条错误消息,告诉我违反了CostTypes的PK规则,这意味着它正在尝试插入费用类型。我还在学习实体框架,但到目前为止,使用它只是一种挫折和头痛。有人知道我的问题是什么以及如何解决它?
修改
这是Slauma要求的信息。我保持简单并排除不相关的对象
Costs
位于一个表格中,CostTypes
位于另一个表格中。在数据库中,Costs.TypeId
列不允许为空,并且是CostTypes
的外键。两个表的Id
字段都是自动生成的。
我的EF模型只是添加了两个数据库表的通用模型。我对它做的唯一更改是重命名一些字段并删除CostTypes.Costs
导航属性。
导入的Excel文件将大部分成本映射到匹配的CostType.Name
,但excel文件中的字符串可能与CostType
不匹配,因此{{1 NULL Lists.CostTypes.FirstOrDefault(t => t.Name == row[0].ToString()) can assign a
Cost.Type value to the
NULL property. That doesn't seem to be a problem though, because the form still comes up with the list of costs and their default selected items. Item's with a
ComboBox`并触发验证错误,必须在保存前更正。
加载CostType do not have an item selected in the CostType
列表的代码是
CostType
可以找到public static List<T> GetList<T>(string sortProperty)
where T : EntityObject
{
using (var context = new TContext())
{
return ApplyOrder<T>(context.CreateObjectSet<T>(), sortProperty, "OrderBy").ToList();
}
}
代码here。
从
调用GetList方法ApplyOrder
答案 0 :(得分:2)
我想通了......它是几个不同的东西混合
创建新的Cost
并设置Type
正在为共享数据上下文添加成本。如果该成本未包含在要保存的成本列表中,或者未通过验证错误,或者用户已从“导入”对话框中取消,则成本仍然存在于context.ObjectStateManager._addedObjects
中,即使我从未调用过AddObject
1}}或AttachObject
。一旦我意识到我开始调用DeleteObject
关于不会保存的成本并且它清除了我得到的第一个错误。
我遇到的第二个错误(重复PK)是因为我正在循环查看新的费用并在每个费用上调用AddObject
和SaveChanges
。由于将Cost.Type
设置为附加的CostType
会自动将我的费用添加到上下文中,因此获得保存的第一个成本实际上是将所有新费用添加到数据库,而第二个费用是尝试调用{{ 1}} / AddObject
关于EF看作已经存在的对象
答案 1 :(得分:1)
这不是一个令人满意的答案,而是基于您在问题中的信息和对您的问题的评论中的猜测和开放式问题的混合:
首先:您的列表Lists.CostTypes
显然包含分离的实体,这些实体是您稍后添加并保存新对象的上下文。因为您有{{ 1}}阻止:using
您正在另一个环境中检索using (var context = new TContext())
个实体。
要告知EF这些CostType
实体已存在于数据库中,您必须将实体附加到您保存更改的第二个上下文(CostType
) in(或在您检索列表的方法中使用相同的上下文)。我在你的代码中没有看到你这样做。 (context.CostTypes.Attach(costType)
是导航引用属性,而不是外键属性,对吧?)
另一方面,当未附加CostType
个实体时,您应该在数据库中获得重复的CostType,因为当您调用{{1时,EF会将它们视为新对象(在数据库中插入)因为EF总是将分离实体的整个对象图放入CostType
状态,因此对于AddObject
实体。您是否在工作示例中的DB中获得了重复的CostType?如果没有,您的代码段中缺少重要的内容。
最后一段假设Cost
的密钥在数据库中自动生成,正如您所说。如果没有,您将获得PK约束违规而不是重复实体。
如果Added
和CostType
的密钥真的是自动生成的身份,我想知道你提到的PK违规可能来自哪里。每次插入都会创建一个新的唯一主键。永远不会发生PK中提琴。您能否详细显示异常消息?
您是否检查了要保存的所有CostType
实体确实具有非空Cost
属性(在用户修复了所有验证错误之后) ?我在代码中看不到任何其他可能的原因,为什么你会得到“关系 - 无法改变的例外”,除了至少Cost
个对象之一CostType
是{ {1}}。