2015-10-15 148 views
5

我有运行实体框架WCF服务器应用程序6.实体框架6 - DataServiceContext检测也已经被修改

我的客户端应用程序消耗的OData从通过DataServiceContext的服务器,在我的客户端代码中,我希望能够调用上下文中的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?

+0

也许我不明白用例,但为什么不直接调用SaveChanges()呢?如果没有改变,EF不会做任何事情。据推测,EF在内部做了类似的事情,而你正在重新发明轮子。 – Vlad274

+0

感谢Vlad,我想在实际保存数据之前弹出一个对话框,说出“你确定要保存更改”。如果没有变化,我不想弹出对话框。 –

回答

2

远了。我刚刚阅读了DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged),这是从UpdateObject(entity)直接调用false参数。

逻辑读象:

  • 如果已经修改,返回; (短路)
  • 如果不变,则抛出if failIfNotUnchanged; (仅从ChangeState()开始)
  • 其他设置状态为已修改。 (无数据检查发生)

所以由它的外观,UpdateObject不关心/检查实体的内部状态,只是State枚举。这使得更新在没有变化时感觉有点不准确。

然而,我觉得你的问题是那么的代码OP第二块,你检查你的分机呼叫UpdateObjectHasChanges之前。这些实体只是荣耀的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!

+0

托德你很棒=)你把我放在了我需要看的地方。我最终用'HasChanges()'属性创建了一个部分类。在构造函数中,我为PropertyChanged'PropertyChanged + = Customer_PropertyChanged;'创建了一个委托,并在我的部分类中实现了一个事件处理函数,如下所示:'private void Customer_PropertyChanged(object sender,PropertyChangedEventArgs e)=> HasChanges = true;'。在我加载实体后,我将'HasChanges()'设置为false。任何触发PropertyChanged事件的属性都会将实体标记为发生更改。谢谢! –

+1

**附加:**刚刚发现上述解决方案的小问题。在部分类中创建属性'HasChanges()'会导致实体相信它有一个名为** HasChanges **的数据库列。而不是使用属性,我最终使用一个私人变量与一个单独的getter和setter方法。干杯。 –

2

难道你不能正确地添加/编辑你的实体吗? MSDN指出您必须使用AddObjectUpdateObject,或DeleteObject得到改变跟踪射击在客户端上(https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - 见管理并发)。否则你的扩展方法看起来不错。

+0

谢谢托德。在我的上下文中的对象在客户端访问之前通过OData加载到上下文中。 (沿用ctx.Customers中的c,其中c.CustomerId == CustomerId选择c).FirstOrDefault();'上下文合并选项保留默认值,用于启用更改跟踪。变更已成功发送回数据库,似乎我自己无法看到这些更改。 –

+0

你需要Attach()吗? –

+0

以前我没有见过'Attach()'方法,感谢这个建议,但是不确定它会帮助我,因为它看起来像是为了让断开连接的实体进入上下文,所以它们可以被更新而不是被插入。我的实体肯定是在上下文中并被正确跟踪和更新。 –

0

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

ctx.Configuration.AutoDetectChangesEnabled 

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

这也意味着它们必须由DataServiceContext的同一实例进行跟踪。你以某种方式创造多个上下文吗?

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

+0

谢谢Jørgen。 Configuration.AutoDetectChangesEnabled不是DataServiceContext的属性。我的上下文被设置为进行更改跟踪,这在我修改数据并调用SaveChanges时起作用。我只使用上下文的一次实例。所有映射都很好,正如我所说的代码正在读取和写入数据一样,我自己也无法检测到这些更改。 –

0

我怀疑客户端的datacontext是不一样的,所以变化始终是false

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

另一种方法是,您必须自己跟踪更改,只需使用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(); 
    } 
+0

谢谢huoxudong125。我使用的上下文是用于加载实体的相同实例,当调用SaveChanges()时成功保存更改。我在OData客户端上创建DataServiceContext,在该场景中'ChangeTracker'不是DataServiceContext的成员。 –