【问题标题】:EntityFramework doesn't refresh navigation propertiesEntityFramework 不刷新导航属性
【发布时间】:2015-10-01 01:27:30
【问题描述】:

我的关系很简单

我已经使用上面的模型创建了一个简单的应用程序。每次 DB 更改时,都必须更新应用程序中的模型。我可以通过调用 GetDBChanges 存储过程来获取最新的更改。 (见方法 T1Elapsed)

这是应用程序:

class Program
{
    private static int? _lastDbChangeId;
    private static readonly MASR2Entities Model = new MASR2Entities();
    private static readonly Timer T1 = new Timer(1000);
    private static readonly Timer T2 = new Timer(1000);
    private static Strategy _strategy = null;

    static void Main(string[] args)
    {
        using (var ctx = new MASR2Entities())
        {
            _lastDbChangeId = ctx.GetLastDbChangeId().SingleOrDefault();
        }
        _strategy = Model.Strategies.FirstOrDefault(st => st.StrategyId == 224);

        T1.Elapsed += T1Elapsed;
        T1.Start();

        T2.Elapsed += T2Elapsed;
        T2.Start();

        Console.ReadLine();
    }

    static void T2Elapsed(object sender, ElapsedEventArgs e)
    {
        Console.WriteLine("All rules: " + Model.StrategyRules.Count());
        Console.WriteLine("Strategy: name=" + _strategy.Name + " RulesCount=" + _strategy.StrategyRules.Count);
    }

    private static void T1Elapsed(object sender, ElapsedEventArgs e)
    {
        T1.Stop();
        try
        {
            using (var ctx = new MASR2Entities())
            {
                var changes = ctx.GetDBChanges(_lastDbChangeId).ToList();
                foreach (var dbChange in changes)
                {
                    Console.WriteLine("DbChangeId:{0} {1} {2} {3}", dbChange.DbChangeId, dbChange.Action, dbChange.TableName, dbChange.TablePK);
                    switch (dbChange.TableName)
                    {
                        case "Strategies":
                            {
                                var id = Convert.ToInt32(dbChange.TablePK.Replace("StrategyId=", ""));
                                Model.Refresh(RefreshMode.StoreWins, Model.Strategies.AsEnumerable());
                            }
                            break;
                        case "StrategyRules":
                            {
                                var id = Convert.ToInt32(dbChange.TablePK.Replace("StrategyRuleId=", ""));
                                Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable());
                            }
                            break;
                    }
                    _lastDbChangeId = dbChange.DbChangeId;
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("ERROR: " + ex.Message);
        }
        finally
        {
            T1.Start();
        }
    }
}

当我运行它时,这是一个示例输出:

All rules: 222
Strategy: name=Blabla2 RulesCount=6

然后我在子表中添加一行(策略规则),

DbChangeId:1713 I StrategyRules StrategyRuleId=811
All rules: 223
Strategy: name=Blabla2 RulesCount=7

最后,我从 StrategyRules 中删除该行

DbChangeId:1714 D StrategyRules StrategyRuleId=811
All rules: 222
Strategy: name=Blabla2 RulesCount=7

为什么 RulesCount 还是 7?如何强制 EF 刷新“导航属性”?

我在这里缺少什么?

---EDIT--- 涵盖 Slauma 的回答

case "StrategyRules":
{
   var id = Convert.ToInt32(dbChange.TablePK.Replace("StrategyRuleId=", ""));
   if (dbChange.Action == "I")
   {
       //Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable());       
   }
   else if (dbChange.Action == "D")
   {
      var deletedRule1 = Model.StrategyRules.SingleOrDefault(sr => sr.Id == id); 
      //the above one is NULL as expected

      var deletedRule2 = _strategy.StrategyRules.SingleOrDefault(sr => sr.Id == id);
      //but this one is not NULL - very strange, because _strategy is in the same context
      //_strategy = Model.Strategies.FirstOrDefault(st => st.StrategyId == 224);
   }  
}

【问题讨论】:

    标签: c# .net entity-framework-4


    【解决方案1】:

    ObjectContext.Refresh 刷新您传递给方法along with any keys that refer to related entities 的实体的标量属性。如果您传递给该方法的实体在数据库中不再存在,因为它同时已被删除,Refresh 对附加的实体不做任何事情,只是忽略它。 (这是我的猜测,但我无法解释为什么你 1)在Refresh 上没有异常(比如“无法刷新实体,因为它已被删除”)和 2)实体显然仍然附加上下文。)

    您的 Insert case 不起作用,因为您调用了 Refresh,但它起作用了,因为您在这一行将整个 StrategyRules 表加载到内存中:

    Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable())
    

    Refresh 在内部枚举第二个参数中的集合。通过开始迭代,它会触发查询,即 Model.StrategyRules = 加载整个表。 AsEnumerable() 只是从 LINQ-to-Entities 到 LINQ-to-Objects 的切换,也就是说,在 AsEnumerable() 之后应用的每个 LINQ 运算符都在内存中执行,而不是在数据库中执行。由于您没有应用任何内容,AsEnumerable() 实际上对您的查询没有任何影响。

    因为您加载了整个表,所以最近插入的 StrategyRule 也将被加载,并且将一起加载 _strategy 实体的密钥。 ObjectContext 的自动关系修复建立了与_strategy 中导航集合的关系,_strategy.StrategyRules.Count 将是7。 (您可以删除Refresh 调用,只调用Model.StrategyRules.ToList(),结果仍然是7。)

    现在,这一切都不适用于删除案例。您仍然运行查询以从数据库中加载整个 StrategyRules 表,但 EF 不会再从上下文中删除或分离不在结果集中的实体。 (据我所知,没有强制这种自动删除的选项。)被删除的实体仍在上下文中,其键引用strategy,计数将保持7

    我想知道的是为什么你不利用你的 DBChanges 显然知道 dbChange.TablePK 属性中删除了什么。除了使用Refresh,你不能使用类似的东西:

    case "StrategyRules":
    {
        switch (dbChange.Action)
        {
            case "D":
            {
                var removedStrategyRule = _strategy.StrategyRules
                    .SingleOrDefault(sr => sr.Id == dbChange.TablePK);
                if (removedStrategyRule != null)
                    _strategy.StrategyRules.Remove(removedStrategyRule);
            }
            break;
    
            case ...
        }
    }
    break;
    

    【讨论】:

    • 首先非常感谢您的解释。这是有道理的,你是对的,我不需要调用 Refresh (没有它会有相同的行为)但是你如何解释这个:Model.StrategyRules.SingleOrDefault(sr => sr.Id == myId) 在 DB 之后为空删除但 _strategy.StrategyRules.SingleOrDefault(sr => sr.Id == myId) 不为空(它仍然保留“已删除”规则)。基本上 _strategy.StrategyRules.Count = 7 但 Model.StrategyRules.ToList() 返回 6 个项目。那么它是否从上下文中删除?早上我会添加更多代码以使其更清晰。
    • 其次,关于 switch-case "D" 的好建议,但我没有 _strategy 对象。 (这只是示例)。我也不能做 Model.Strategies.SingleOrDefault(s => s.Id == ???);因为不知道 StrategyId(只有 StrategyRuleId)。我正在考虑通过调用 Model.StrategyRules.SingleOrDefault(sr => sr.Id == myID).Strategy 来获取策略对象,但由于 StrategyRule 为空(已从上下文中删除)而失败
    • @Novitzky:您的第一条评论。删除的规则没有从上下文中删除。 Model.StrategyRules.ToList() 查询数据库,而不是上下文条目。因此,它返回除了已删除的规则 -> count = 6 之外的所有内容。_strategy.StrategyRules 是内存中的对象图,它仍然具有已删除的规则 -> count = 7。
    • 再次感谢。我做了一些测试,它的工作原理与您解释的完全一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多