简体   繁体   English

实体框架6 - DataServiceContext检测有更改

[英]Entity Framework 6 - DataServiceContext Detect Has Changes

I have a WCF server application running Entity Framework 6. 我有一个运行Entity Framework 6的WCF服务器应用程序。

My client application consumes OData from the server via a DataServiceContext, and in my client code I want to be able to call a HasChanges() method on the context to see if any data in it has changed. 我的客户端应用程序通过DataServiceContext从服务器消耗OData,在我的客户端代码中,我希望能够在上下文中调用HasChanges()方法,以查看其中的任何数据是否已更改。

I tried using the following extension method: 我尝试使用以下扩展方法:

    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);
    }

But it always returns false, even if an entity it is tracking does have changes. 但它总是返回false,即使它正在跟踪的实体确实有变化。

For instance, given that I have a tracked entity named Customer, the following code always returns before calling SaveChanges(). 例如,假设我有一个名为Customer的跟踪实体,则在调用SaveChanges()之前始终返回以下代码。

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

If I comment out the if not ctx.HasChanges() then return line of code, the changes are saved successfully so I'm happy that the entity has received the change and is able to save it. 如果我注释掉if if ctx.HasChanges()然后返回代码行,则更改会成功保存,所以我很高兴实体已收到更改并能够保存它。

It seems that the change is getting tracked by the context, just that I can't determine that fact from my code. 看来,变化越来越上下文跟踪,只是我不能确定从我的代码这一事实。

Can anyone tell me how to determine HasChanges on a DataServiceContext? 谁能告诉我如何在DataServiceContext上确定HasChanges?

Far out. 远。 I just read through DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged) , which is called directly from UpdateObject(entity) with a false argument. 我刚刚阅读了DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged) ,它是使用false参数直接从UpdateObject(entity)调用的。

The logic reads like: 逻辑如下:

  • If already modified, return; 如果已修改,则返回; (short-circuit) (短路)
  • If not unchanged, throw if failIfNotUnchanged ; 如果没有改变,抛出failIfNotUnchanged ; (true only from ChangeState() ) (仅来自ChangeState()
  • Else set state to modified. 否则将状态设置为已修改。 (no data checks happened) (没有发生数据检查)

So by the looks of it, UpdateObject doesn't care about/check the internal state of the entity, just the State enum. 所以从它的外观来看, UpdateObject并不关心/检查实体的内部状态,只关心State枚举。 This makes updates feel a little inaccurate when there are no changes. 这使得更新在没有更改时感觉有点不准确。

However , I think your problem is then in the OP 2nd block of code, you check your extension HasChanges before calling UpdateObject . 不过 ,我觉得你的问题是那么的代码OP第二块,你检查你的扩展HasChanges 之前调用UpdateObject The entities are only glorified POCOs (as you can read in your Reference.cs (Show Hidden Files, then under the Service Reference)). 这些实体只是美化的POCO(您可以在Reference.cs阅读(显示隐藏文件,然后在服务参考下)。 They have the obvious properties and a few On- operations to notify about changing. 他们有明显的性能和几个On-操作通知有关更改。 What they do not do internally is track state. 他们内部没有做的是跟踪状态。 In fact, there is an EntityDescriptor associated to the entity, which is responsible for state-tracking in EntityTracker.TryGetEntityDescriptor(entity) . 实际上,有一个与实体相关联的EntityDescriptor ,它负责EntityTracker.TryGetEntityDescriptor(entity)状态跟踪。

Bottom line is operations actually work very simply, and I think you just need to make your code like 底线是操作实际上非常简单,我认为你只需要使你的代码像

Customer.Address1 = "Fred";
ctx.UpdateObject(Customer);
if (!ctx.HasChanges()) return;
ctx.SaveChanges();

Though as we know now, this will always report HasChanges == true, so you may as well skip the check. 虽然我们现在知道,但这总是会报告HasChanges == true,所以你也可以跳过检查。

But don't despair! 但不要绝望! The partial classes provided by your service reference may be extended to do exactly what you want. 您的服务引用提供的部分类可以扩展为完全符合您的要求。 It's totally boilerplate code, so you may want to write a .tt or some other codegen. 它完全是样板代码,因此您可能想要编写.tt或其他一些代码。 Regardless, just tweak this to your entities: 无论如何,只需将其调整为您的实体:

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
}

But now this works great and is similarly expressive to the OP: 但是现在这很有效,并且与OP表达相似:

var book = context.Books.Where(x => x.Id == 2).SingleOrDefault();
book.BookName = "W00t!";
Console.WriteLine(book.HasChanges);

HTH! HTH!

Is it possible you're not adding/editing your entities properly? 您是否可能无法正确添加/编辑实体? MSDN states that you must use AddObject , UpdateObject , or DeleteObject to get change tracking to fire on the client ( https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - see Managing Concurrency ). MSDN声明必须使用AddObjectUpdateObjectDeleteObject才能在客户端上触发更改跟踪( https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - 请参阅管理并发 )。 Otherwise your extension method looks good. 否则你的扩展方法看起来不错。

In order for this to work, automatic change tracking must be enabled. 为此,必须启用自动更改跟踪。 You can find this setting in 您可以在中找到此设置

ctx.Configuration.AutoDetectChangesEnabled

All the entity objects must also be tracked by the context ctx . 所有实体对象也必须由上下文ctx跟踪。 This means that they must be returned by one of ctx 's methods or explicitly added to the context. 这意味着它们必须由ctx的一个方法返回或显式添加到上下文中。

It also means that they must be tracked by the same instance of DataServiceContext . 它还意味着它们必须由DataServiceContext的同一实例跟踪。 Are you somehow creating more than one context? 你是否以某种方式创建了多个上下文?

The model must also be configured correctly. 还必须正确配置模型。 Perhaps Customer.Address1 is not mapped to a database column. 也许Customer.Address1未映射到数据库列。 In that case, EF will not detect changes to the column. 在这种情况下,EF不会检测到列的更改。

I doubt that the datacontext in client is not the same one.so the changes is always is false . 我怀疑客户端中的datacontext不是同一个。所以更改总是false

You must be sure the Datacontext is the same one(instance), for every changes of the Datacontext . 你必须确保Datacontext是同一个(实例),对于每一个变化Datacontext Then to detect the changes is meaningful. 然后检测变化是有意义的。

Another way ,you must tracked the changes by yourself.simply using the Trackable Entities to help you tracking the changes of entities in the datacontext. 另一种方法是,您必须自己跟踪更改。仅使用可跟踪实体来帮助您跟踪datacontext中实体的更改。

BTW. BTW。 I Use the Code ' ctx.ChangeTracker.HasChanges()' to detect the changes of 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();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM