0

我有一个简单的关系

在此处输入图像描述

我用上面的模型创建了一个简单的应用程序。每次 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 刷新“导航属性”?

我在这里缺少什么?

---编辑---涵盖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);
   }  
}
4

1 回答 1

2

ObjectContext.Refresh刷新您传递给方法的实体的标量属性以及引用相关实体的任何键。如果您传递给该方法的实体在数据库中不再存在,因为它同时已被删除,则Refresh对附加的实体没有任何作用,只是忽略它。(这是我的猜测,但我无法解释为什么您 1)没有出现异常Refresh(例如“无法刷新实体,因为它已被删除”)和 2)实体显然仍附加到上下文.)

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

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

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

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

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

我想知道的是,为什么您不利用您的一组DBChanges明显知道该属性中已删除的确切内容dbChange.TablePKRefresh你不能使用类似的东西,而不是使用:

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;
于 2013-02-28T20:05:04.027 回答