代码之家  ›  专栏  ›  技术社区  ›  Max

仅当不打印调试语句时,实体框架种子方法出错

  •  1
  • Max  · 技术社区  · 8 年前

    在实体框架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循环,那么seed方法就可以正常运行。for循环被注释掉后,我们得到以下异常:

    保存不公开外键的实体时出错 属性的关系。EntityEntries属性将 返回null,因为无法将单个实体标识为源 异常的。可以在保存时处理异常 通过在实体类型中公开外键属性更容易。看见 有关详细信息,请参见InnerException。

    在本例中,User对象与Preferences对象的距离为1-1,Preference具有User表的外键。

    我们甚至可以取出流编写器,将属性循环到控制台,只要循环存在,seed方法就会正确运行。一旦它消失,我们就会得到错误。

    1 回复  |  直到 8 年前
        1
  •  1
  •   Stefan Mohr    8 年前

    我猜这里可能会发生什么。我认为这是你的问题:

    prefs.ColumnIds = defaultColumnIds; prefs.Columns = defaultColumns;

    我想这些是由数据库中的同一字段支持的?那么您正在设置Column实体和ColumnId主键?你不需要同时设置两者(尽管 应该 工作)。

    我打赌如果你取消分配给 prefs.Columns (并忽略调试代码),您的代码将开始工作。问题是 defaultColumns .里面有什么?列实体-但这些实体是否附加到当前的DbContext?(你的代码没有显示它们是如何形成的)

    当你用 context.Preferences.FirstOrDefault(x => x.UserId == user.Id); ,您要求EF提供Preference实体,但默认情况下EF不会急于加载导航属性(如那些Column实体)。相反,如果您有一系列Column实体,但它们尚未显式附加到(或使用)当前的DbContext,EF会认为这些Column是新的,并且这种关系可能不会以EF可以插入新Column的方式映射(无论如何,您都不希望发生这种情况)。

    当调试代码运行时, prefs.GetType().GetProperties() 正在枚举Preference中的所有属性,我认为EF会延迟加载。当您删除循环时,它不再枚举,因此Preference的所有导航属性都没有预加载。

    有几种方法可以实现这一点:

    • 如果你绝对肯定所有这些 defaultColumnIds 运行Seed方法时,数据库中已存在,然后仅设置 ColumnIds 今天到此为止。当它执行查询时,它将匹配数据库中的实体,只要期望的外键在那里,就可以了。

    • 获取 默认列 一旦打开上下文,或者如果在代码中定义了它们,请在调用SaveChanges之前将它们附加到ChangeTracker。