如何在后面的代码中设置导航属性的值?

时间:2011-08-22 19:10:38

标签: c# entity-framework navigation-properties

我有一个名为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();
}
  • 如果我删除了在读取excel文件时手动设置Cost的代码行,则保存工作正常。
  • 如果我改变代码行来读取Lists.Costs [2],它会保存得很好。
  • 如果删除GetNew()中设置默认值的代码行,我会收到一条错误消息,告诉我违反了CostTypes的PK规则,这意味着它正在尝试插入费用类型。
  • 将显示Type的ComboBox更改为其他内容仍会产生相同的错误。
  • 从excel文件加载成本后,当我更改Type并尝试保存时,我的常规添加/编辑表单会抛出相同的错误。如果我没有加载excel文件,它们可以正常工作。

我还在学习实体框架,但到目前为止,使用它只是一种挫折和头痛。有人知道我的问题是什么以及如何解决它?

修改

这是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

2 个答案:

答案 0 :(得分:2)

我想通了......它是几个不同的东西混合

创建新的Cost并设置Type正在为共享数据上下文添加成本。如果该成本未包含在要保存的成本列表中,或者未通过验证错误,或者用户已从“导入”对话框中取消,则成本仍然存在于context.ObjectStateManager._addedObjects中,即使我从未调用过AddObject 1}}或AttachObject。一旦我意识到我开始调用DeleteObject关于不会保存的成本并且它清除了我得到的第一个错误。

我遇到的第二个错误(重复PK)是因为我正在循环查看新的费用并在每个费用上调用AddObjectSaveChanges。由于将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约束违规而不是重复实体。

  • 如果AddedCostType的密钥真的是自动生成的身份,我想知道你提到的PK违规可能来自哪里。每次插入都会创建一个新的唯一主键。永远不会发生PK中提琴。您能否详细显示异常消息?

  • 您是否检查了要保存的所有CostType实体确实具有非空Cost属性(在用户修复了所有验证错误之后) ?我在代码中看不到任何其他可能的原因,为什么你会得到“关系 - 无法改变的例外”,除了至少Cost个对象之一CostType是{ {1}}。