在我们的Entity Framework 6种子方法中,我们将用户首选项设置为默认值。我们有很多问题让东西正常工作,所以我们开始将调试语句打印到文件中。但是,现在,如果我们删除调试行,就会出现异常。
以下是代码:
// Get the preferences.
Preferences prefs = context.Preferences.FirstOrDefault(x => x.UserId == user.Id);
using (StreamWriter write = new StreamWriter(@"C:\myFile.txt"))
{
//foreach (PropertyInfo prop in prefs.GetType().GetProperties())
// write.WriteLine($"{prop.Name} = {prop.GetValue(prefs)}");
prefs.ColumnIds = defaultColumnIds;
prefs.Columns = defaultColumns;
prefs.CategoriesOnYAxis = true;
prefs.TabHorizontal = true;
prefs.OnlyAssignedToUser = true;
context.SaveChanges();
}
如果我们取消注释For循环,那么种子方法运行正常。在for循环注释掉后,我们得到以下异常:
保存不公开外键的实体时发生错误 他们关系的属性。 EntityEntries属性将 返回null,因为无法将单个实体标识为源 例外。可以在保存时处理异常 通过在实体类型中公开外键属性更容易。看到 InnerException以获取详细信息。
在此示例中,User对象是Preferences对象的1-1,而Preferences具有User表的外键。
我们甚至可以取出流编写器并将属性循环到控制台,只要该循环存在,种子方法就可以正常运行。一旦它消失,我们就会收到错误。
答案 0 :(得分:1)
我猜这里可能会发生什么。我认为这是你的问题:
prefs.ColumnIds = defaultColumnIds;
prefs.Columns = defaultColumns;
我认为这些是由数据库中的相同字段支持的?那么您是否设置了Column实体以及ColumnId主键?你不需要同时设置它们(虽然它应该工作)。
我敢打赌,如果删除prefs.Columns
的作业(并省略调试代码),您的代码将开始工作。问题是defaultColumns
。那里有什么?列实体 - 但是那些附加到当前DbContext的实体? (您的代码并未显示它们是如何形成的)
当您使用context.Preferences.FirstOrDefault(x => x.UserId == user.Id);
获取首选项时,您要求EF为首选项实体,但默认情况下,EF不会获得预先加载导航属性(如列实体)。相反,如果你有一堆Column实体,但它们没有明确地附加到(或使用)当前的DbContext,那么EF会认为那些Columns是新的,并且这种关系可能不会以EF的方式映射可以插入新列(无论如何你都不想发生这种情况)。
当你的调试代码运行时,prefs.GetType().GetProperties()
正在枚举Preference中的所有属性,我认为EF是延迟加载的。当您删除循环时,它不再枚举,因此所有首选项的导航属性都没有预先加载。
有几种方法可以解决这个问题:
如果您确定运行Seed方法时所有defaultColumnIds
已经在数据库中,那么只设置ColumnIds
并将其称为一天。它会在执行查询时匹配数据库中的实体,只要预期的外键在那里就可以了。
打开上下文后立即从上下文中获取defaultColumns
,或者,如果您在代码中定义了它们,请在调用SaveChanges之前将它们附加到ChangeTracker。