Entity Framework 6 - Data Discovery DataServiceContext Has Changes

I have a WCF server application with Entity Framework 6.

My client application consumes OData from the server through the DataServiceContext, and in my client code I want to be able to call the HasChanges () method in the context to see if any data in it has changed.

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 the object it is tracking has changes.

For example, if I have a tracked object named Customer, the following code always returns before calling SaveChanges ().

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

If I comment if not ctx.HasChanges (), return the line of code, the changes will be successfully saved, so I'm happy that the object received the change and can save it.

It seems that change tracked by context, I just canโ€™t determine this fact from my code.

Can someone tell me how to define HasChanges in a DataServiceContext?

+5
source share
4 answers

Long away. I just read DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged) , which is called directly from UpdateObject(entity) with argument false .

The logic reads as follows:

  • If already changed, return; (Short circuit)
  • If it has not changed, drop it if failIfNotUnchanged ; (true only from ChangeState() )
  • The rest of the setting state will change. (no data checks occurred)

Thus, when looking at it, UpdateObject does not care / check the internal state of the object, just an enumeration of State . This makes updates a bit inaccurate when there are no changes.

However, I think your problem is that in the 2nd block of OP code, you check the HasChanges extension before calling UpdateObject . Objects are only illustrious POCOs (as you can read in your Reference.cs (Show hidden files and then in the help service)). They have obvious properties and several On- operations to notify you of a change. What they do not do inside is the state of the track. In fact, there is an EntityDescriptor object associated with the object that is responsible for tracking the state in EntityTracker.TryGetEntityDescriptor(entity) .

The bottom line of the operation actually works 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(); 

Although, as we know now, this will always tell HasChanges == true, so you can also skip checking.

But do not despair! The partial classes provided by your service can be extended to do exactly what you want. This is fully boilerplate code, so you can write .tt or some other code. Regardless, just configure this for your objects:

 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 fine and expresses the OP similarly:

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

NTN!

+2
source

Is it possible that you are not adding / editing your objects properly? MSDN states that you must use AddObject , UpdateObject or DeleteObject to track change tracking on the client ( https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - see Management Concurrency ). Otherwise, your extension method looks good.

+2
source

For this to work, you must enable automatic change tracking. This option can be found in

 ctx.Configuration.AutoDetectChangesEnabled 

All entity objects must also be monitored by the ctx context. This means that they must be returned by one of the ctx methods or explicitly added to the context.

This also means that they must be monitored by the same instance of the DataServiceContext . Did you somehow create multiple contexts?

The model must also be properly configured. Perhaps Customer.Address1 not mapped to a database column. In this case, EF does not detect changes in the column.

0
source

I doubt the datacontext in the client is not the same. So changes are always false .

You must be sure that the Datacontext is the same (instance) for each change in the Datacontext . Then it makes sense to detect changes.

In another way, you must track the changes yourself. Just use tracked objects to help you track object changes in the datacontext.

BTW. I use the code 'ctx.ChangeTracker.HasChanges ()' to detect changes to the 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
source

Source: https://habr.com/ru/post/1233794/


All Articles