C# EntityFramework没有';刷新导航属性
我有一段简单的关系 我用上面的模型创建了一个简单的应用程序。应用程序中的模型必须在每次数据库更改时更新。我可以通过调用GetDBChanges存储过程来获取最新的更改。(见方法T11) 以下是应用程序:C# EntityFramework没有';刷新导航属性,c#,.net,entity-framework-4,C#,.net,Entity Framework 4,我有一段简单的关系 我用上面的模型创建了一个简单的应用程序。应用程序中的模型必须在每次数据库更改时更新。我可以通过调用GetDBChanges存储过程来获取最新的更改。(见方法T11) 以下是应用程序: class Program { private static int? _lastDbChangeId; private static readonly MASR2Entities Model = new MASR2Entities(); private static
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
然后我在子表中添加一行(策略规则)
最后,我将该行从StrategyRules中删除
DbChangeId:1714 D StrategyRules StrategyRuleId=811
All rules: 222
Strategy: name=Blabla2 RulesCount=7
为什么RuleScont仍然是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);
}
}
ObjectContext.Refresh
刷新传递到方法中的实体的标量属性。如果您传递到方法中的某个实体在数据库中不再存在,因为它同时已被删除刷新
不会对附加的实体执行任何操作,只会忽略它。(这是我的猜测,但我无法解释为什么1)在刷新
时没有出现异常(例如“无法刷新实体,因为它已被删除”),以及2)实体显然仍然附加到上下文。)
您的Insert案例不起作用,因为您调用了Refresh
,但它起作用,因为您将整个StrategyRules
表加载到此行的内存中:
Model.Refresh(RefreshMode.StoreWins, Model.StrategyRules.AsEnumerable())
Refresh
在内部枚举第二个参数中的集合。通过启动迭代,它触发查询,该查询只是Model.StrategyRules
=加载整个表AsEnumerable()
只是从LINQ到实体再到LINQ到对象的切换,也就是说,在AsEnumerable()
之后应用的每个LINQ操作符都是在内存中执行的,而不是在数据库中执行的。由于不应用任何内容,AsEnumerable()
实际上对查询没有影响
因为您加载了整个表,所以最近插入的StrategyRule
也将被加载,并且一起将是\u strategy
实体的键。ObjectContext
的自动关系修复程序在\u strategy
和\u strategy.StrategyRules.Count
中建立与导航集合的关系。(您可以删除Refresh
调用,只需调用Model.StrategyRules.ToList()
,结果仍然是7
)
现在,所有这些在Delete情况下都不起作用。您仍然可以运行一个查询来从数据库加载整个StrategyRules
表,但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;
首先非常感谢你的解释。这是有道理的,你是对的,我不需要调用Refresh(没有它也会有同样的行为),但是你如何解释:Model.StrategyRules.SingleOrDefault(sr=>sr.Id==myId)在DB delete之后为null,而_strategy.StrategyRules.SingleOrDefault(sr=>sr.Id==myId)在notnull中为null(它仍然保存“deleted”规则)。基本上_strategy.StrategyRules.Count=7,但Model.StrategyRules.ToList()返回6项。那么它是否会从上下文中移除?我会在早上添加更多的代码,让它更清晰。其次,很好的建议与开关情况“D”的东西,但我没有_战略目标。(这只是一个例子)。我也不能做Model.Strategies.SingleOrDefault(s=>s.Id==??);因为不知道StrategyId(仅StrategyRuleId)。我正在考虑通过调用Model.StrategyRules.SingleOrDefault(sr=>sr.Id==myID)获取strategy对象。strategy失败,因为StrategyRule为null(已从上下文中删除)@Novitzky:对于您的第一条评论。删除的规则不会从上下文中删除
Model.StrategyRules.ToList()
查询数据库,而不是上下文条目。因此,它返回除已删除的规则->计数=6之外的所有内容\u strategy.StrategyRules
是内存中的对象图,它仍然具有已删除的规则->计数=7。再次感谢。我做了一些测试,它的工作原理和你解释的一模一样。
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;