实体框架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 ?

实体框架6 - DataServiceContext检测有变化

远远的。我刚刚通读了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指出,您必须使用AddObjectUpdateObjectDeleteObject才能在客户端上启动更改跟踪(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();
    }