我有一个简单的关系
我用上面的模型创建了一个简单的应用程序。每次DB更改时,必须更新应用程序中的模型。我可以通过调用GetDBChanges存储过程来获取最新的更改。 (见方法T1Elapsed)
这是app:
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);
}
}
答案 0 :(得分:2)
ObjectContext.Refresh
刷新传递给方法along with any keys that refer to related entities的实体的标量属性。如果传入方法的实体在数据库中不再存在,因为它已被删除,同时Refresh
对附加的实体不执行任何操作,只是忽略它。 (这是我的猜测,但我无法解释为什么你1)不在Refresh
上得到例外(例如“因为它已被删除而无法刷新实体”)和2)实体显然是仍然依附于上下文。)
您的插入案例不起作用,因为您调用了Refresh
但是它有效,因为您将整个StrategyRules
表加载到此行的内存中:
Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable())
Refresh
在内部枚举第二个参数中的集合。通过启动迭代,它会触发仅Model.StrategyRules
=加载整个表的查询。 AsEnumerable()
只是从LINQ到实体到LINQ到对象的切换,也就是说,在内存中执行AsEnumerable()
而不是在数据库上执行后,将应用的每个LINQ运算符。由于您不应用任何内容,AsEnumerable()
实际上对您的查询没有任何影响。
因为您加载了整个表格,所以最近插入的StrategyRule
也将被加载,并且_strategy
实体的密钥将一起加载。 ObjectContext
的自动关系修正建立了与_strategy
中导航集合的关系,_strategy.StrategyRules.Count
将7
。Refresh
(您可以移除Model.StrategyRules.ToList()
来电,只需致电7
,结果仍为StrategyRules
。)
现在,所有这些在删除案例中都不起作用。您仍然运行查询以从数据库加载整个strategy
表,但EF不会从上下文中删除或分离不在结果集中的实体。 (据我所知,没有强制执行此类自动删除的选项。)删除的实体仍在上下文中,其键引用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;
,你不能使用像:
{{1}}