Entity Framework 6 - DataServiceContext 检测有变化

Entity Framework 6 - DataServiceContext Detect Has Changes

我有一个 WCF 服务器应用程序 运行 Entity Framework 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);
    }

但它总是 return 错误,即使它正在跟踪的实体确实发生了变化。

例如,假设我有一个名为 Customer 的跟踪实体,以下代码在调用 SaveChanges() 之前总是 returns。

    Customer.Address1 = "Fred"
    if not ctx.HasChanges() then return
    ctx.UpdateObject(Customer)
    ctx.SaveChanges()

如果我注释掉 if not ctx.HasChanges() then return 这行代码,更改已成功保存,很高兴该实体已收到更改并能够保存它。

似乎更改 被上下文跟踪,只是我无法从我的代码中确定这一事实。

谁能告诉我如何确定 DataServiceContext 上的 HasChanges?

你有没有可能adding/editing你的实体不正确? MSDN 声明您必须使用 AddObjectUpdateObjectDeleteObject 来获取更改跟踪以在客户端上触发(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - 请参阅 管理并发 ).否则你的扩展方法看起来不错。

为了使其工作,必须启用自动更改跟踪。您可以在

中找到此设置
ctx.Configuration.AutoDetectChangesEnabled

所有实体对象也必须由上下文跟踪ctx。 这意味着它们必须由 ctx 的方法之一返回或显式添加到上下文中。

这也意味着它们必须被DataServiceContext的同一个实例跟踪。您是否以某种方式创建了多个上下文?

模型也必须正确配置。也许 Customer.Address1 没有映射到数据库列。在这种情况下,EF 将不会检测到列的更改。

我怀疑客户端中的数据上下文不一样one.so变化总是false

对于 Datacontext 的每一次更改,您必须确保 Datacontext 是同一个(实例)。然后检测变化是有意义的。

另一种方式,您必须通过 yourself.simply 使用 Trackable Entities 来跟踪更改,以帮助您跟踪数据上下文中实体的更改。

顺便说一句。我使用代码“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();
    }

很远。我刚刚通读了 DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged),它是通过 false 参数直接从 UpdateObject(entity) 调用的。

逻辑如下:

  • 如果已经修改,return; (短路)
  • 如果没有改变,则抛出 if failIfNotUnchanged; (仅来自 ChangeState()
  • 否则将状态设置为已修改。 (没有进行数据检查)

所以从表面上看,UpdateObject 不关心 about/check 实体的内部状态,只关心 State 枚举。这使得在没有更改时更新感觉有点不准确。

但是,我认为你的问题是在 OP 2nd 代码块中,你检查你的扩展 HasChanges before 呼叫 UpdateObject。这些实体只是经过美化的 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!