实体框架6 - DataServiceContext检测有变化
本文关键字:检测 有变化 DataServiceContext 框架 实体 | 更新日期: 2023-09-27 18:17:55
我有一个WCF服务器应用程序运行实体框架6。
我的客户端应用程序通过DataServiceContext从服务器消费OData,并且在我的客户端代码中,我希望能够在上下文中调用HasChanges()方法来查看其中的任何数据是否已更改。
我尝试使用以下扩展方法:
public static bool HasChanges(this DataServiceContext ctx)
{
// Return true if any Entities or links have changes
return ctx.Entities.Any(ed => ed.State != EntityStates.Unchanged) || ctx.Links.Any(ld => ld.State != EntityStates.Unchanged);
}
但是它总是返回false,即使它正在跟踪的实体确实发生了变化。
例如,假设我有一个名为Customer的跟踪实体,下面的代码总是在调用SaveChanges()之前返回。
Customer.Address1 = "Fred"
if not ctx.HasChanges() then return
ctx.UpdateObject(Customer)
ctx.SaveChanges()
如果我注释掉如果不是ctx.HasChanges()然后返回行代码,更改被成功保存,所以我很高兴实体已经收到更改并能够保存它。
似乎更改是被上下文跟踪,只是我不能从我的代码中确定这一事实。
谁能告诉我如何确定在DataServiceContext HasChanges ?
远远的。我刚刚通读了DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged)
,它是直接从UpdateObject(entity)
调用的,带有false
参数。
逻辑如下:
- 如果已经修改,返回;(短路)
- 如果没有改变,则丢弃
failIfNotUnchanged
;(仅适用于ChangeState()
) - 否则设置为修改状态。(未进行数据检查)
所以看起来,UpdateObject
并不关心/检查实体的内部状态,只关心State
枚举。当没有任何更改时,这使得更新感觉有点不准确。
然而,我认为你的问题是在OP第二块代码中,你在调用UpdateObject
之前检查你的扩展HasChanges
。实体只是经过美化的poco(您可以在Reference.cs
(显示隐藏文件,然后在服务参考下)中读取)。它们具有明显的属性和一些On-
操作来通知更改。它们而不是在内部做的是跟踪状态。事实上,有一个EntityDescriptor
与实体相关联,它在EntityTracker.TryGetEntityDescriptor(entity)
中负责状态跟踪。
底线是操作实际上非常简单,我认为你只需要让你的代码像
Customer.Address1 = "Fred";
ctx.UpdateObject(Customer);
if (!ctx.HasChanges()) return;
ctx.SaveChanges();
虽然我们现在知道,这将总是报告HasChanges == true,所以您不妨跳过检查。
但不要绝望!您的服务引用提供的部分类可以被扩展以完成您想要的功能。它完全是样板代码,所以您可能想要编写一个。tt或其他代码。无论如何,只需将其调整到您的实体:
namespace ODataClient.ServiceReference1 // Match the namespace of the Reference.cs partial class
{
public partial class Books // Your entity
{
public bool HasChanges { get; set; } = false; // Your new property!
partial void OnIdChanging(int value) // Boilerplate
{
if (Id.Equals(value)) return;
HasChanges = true;
}
partial void OnBookNameChanging(string value) // Boilerplate
{
if (BookName == null || BookName.Equals(value)) return;
HasChanges = true;
}
// etc, ad nauseam
}
// etc, ad nauseam
}
但是现在它工作得很好,并且与OP相似:
var book = context.Books.Where(x => x.Id == 2).SingleOrDefault();
book.BookName = "W00t!";
Console.WriteLine(book.HasChanges);
HTH !
是否有可能您没有正确添加/编辑您的实体?MSDN指出,您必须使用AddObject
、UpdateObject
或DeleteObject
才能在客户端上启动更改跟踪(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx -参见管理并发性)。否则,您的扩展方法看起来不错。
为了使其工作,必须启用自动更改跟踪。您可以在
中找到此设置。ctx.Configuration.AutoDetectChangesEnabled
所有实体对象也必须由上下文ctx
跟踪。这意味着它们必须由ctx
的一个方法返回,或者显式地添加到上下文中。
这也意味着它们必须由DataServiceContext
的同一个实例跟踪。您是否以某种方式创建了多个上下文?
模型也必须正确配置。可能Customer.Address1
没有映射到数据库列。在这种情况下,EF将不会检测到列的更改。
我怀疑客户端的数据上下文不是同一个。所以更改的总是false
.
您必须确保Datacontext
是同一个(实例),对于Datacontext
的每次更改。然后检测变化是有意义的。
另一种方式,您必须自己跟踪更改。只需使用可跟踪实体来帮助您跟踪数据上下文中实体的变化。
顺便说一句。我使用代码' ctx.ChangeTracker.HasChanges()'来检测DataContext的变化。
public bool IsContextDirty(DataServiceContext ctx)
{
#if DEBUG
var changed = ctx.ChangeTracker.Entries().Where(t => t.State != EntityState.Unchanged).ToList();
changed.ForEach(
(t) => Debug.WriteLine("entity Type:{0}", t.Entity.GetType()));
#endif
return ctx != null && ctx.ChangeTracker.HasChanges();
}