Entity Framework 5 InvalidOperationException on Reload - c#

I try to discard some changes with the reload function. I get an InvalidOperationException.
How can i prevent this ?
DbContext.SaveChanges();
//Entity is in Unchanged state
//Make some changes to an entity
//Change state to modified
DbContext.Entry(entity).Reload();
InvalidOperationException
EntityMemberChanged or EntityComplexMemberChanged was called without first calling EntityMemberChanging or EntityComplexMemberChanging on
the same change tracker with the same property name. For information
about properly reporting changes, see the Entity Framework
documentation.
EDIT:
I've enabled and disabled ProxyCreationEnabled, LazyLoadingEnabled.
Tried different approaches as well. All these attempts throw the same exception.
var objContext = ((IObjectContextAdapter)context).ObjectContext;
objContext.Refresh(RefreshMode.ClientWins, entry.Entity);
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
Hope i get a solution. Don't want to dispose the full DbContext to reload all the data.

To quote this MSDN thread / post
"it's worth noting that the error shows up whether or not you use change tracking a via a proxy class or call entitymemberchanged explicitly. I seem to get the error whenever I execute entitymemberchanging and changed on a a thread outside the one that created the objectcontext/objectstatemanager, regardless of whether i execute the two functions synchronously or asynchronously, use locks or have the thread explicitly sleep. It occurs to me that this is some sort of "genuine bug" with the objectstatemanager, and not something for which there would be a simple workaround. Ball's in your court, MSFT."
P.S. Too long for comment.

If the code is as you posted it, where the object is loaded from a DbContext, then reloaded from that same DbContext, you should not be explicitly marking it as Modified; making changes to the Entity is enough to mark it as Modified already. In other words:
var o = new SimpleObject { Stuff = "One" };
db.SimpleObjects.Add(o);
db.SaveChanges();
o.Stuff = "Two"; // implicitly marks as Modified for you, since it's still Attached
// Unnecessary
//db.Entry(o).State = System.Data.EntityState.Modified;
db.Entry(o).Reload(); // Works for me

I found that the reload fails on proxy entities that have navigation properties.
As a work around, reset the current values and then reload like this:
var entry = DbContext.Entry(entity);
entry.CurrentValues.SetValues(entry.OriginalValues);
entry.Reload();

I had a similar situation, with the same exception description. In my case I was trying to delete an entity from the context, and it was somehow related to a propertychanged handler still being called.
I just removed the handler before removing the entity from the context with
MyEntity.PropertyChanged -= MyPropertyChangedHandler;
context.MySet.Remove(MyEntity); //it works after removing the handler
Hope this helps someone.

Related

Appropriate update of entity while checking if it exists in EF Core

I have the following method updating an entity. The only biff I had was that when an non-existing ID were provided, I got an harsh exception.
public bool Update(Thing thing)
{
Context.Things.Update(thing);
int result = Context.SaveChanges();
return result == 1;
}
So I added a check to control the exception thrown (plus some nice logging and other facilitation). Eventually, I plan to skip the throwing up entirely.
public bool UpdateWithCheck(Thing thing)
{
Thing target = Context.Things.SingleOrDefault(a => a.Id == thing.Id);
if (target == null)
throw new CustomException($"No thing with ID {thing.Id}.");
Context.Things.Update(thing);
int result = Context.SaveChanges();
return result == 1;
}
No, this doesn't work, because the entity already is being tracked. I have several options to handle that.
Change to Context.Where(...).AsNoTracking().
Explicitly set the updated fields in target and save it.
Horse around with entity states and tampering with the tracker.
Removing the present and adding the new one.
I can't decide which is the best practice. Googling gave me the default examples that do not contain the check for pre-existing status in the same operation.
The reason for the exception is because by loading the entity from the Context to check if it exists, you now have a tracked reference. When you go to update the detatched reference, EF will complain that a instance is already tracked.
The simplest work-around would be:
public bool UpdateWithCheck(Thing thing)
{
bool doesExist = Context.Things.Any(a => a.Id == thing.Id);
if (!doesExist)
throw new CustomException($"No thing with ID {thing.Id}.");
Context.Things.Update(thing);
int result = Context.SaveChanges();
return result == 1;
}
However, there are two problems with this approach. Firstly, because we don't know the scope of the DbContext instance or can guarantee the order of methods, it may be possible that at some point that DbContext instance could have loaded and tracked that instance of the thing. This can manifest as seemingly intermittent errors. The proper way to guard against that would be something like:
public bool UpdateWithCheck(Thing thing)
{
bool doesExist = Context.Things.Any(a => a.Id == thing.Id);
if (!doesExist)
throw new CustomException($"No thing with ID {thing.Id}.");
Thing existing = Context.Things.Local.SingleOrDefault(a => a.Id == thing.Id);
if (existing != null)
Context.Entry(existing).State = EntityState.Detached;
Context.Things.Update(thing);
int result = Context.SaveChanges();
return result == 1;
}
This checks the local tracking cache for any loaded instances, and if found, detaches them. The risk here is that any modifications that haven't be persisted in those tracked references will be discarded, and any references floating around that would have assumed were attached, will now be detached.
The second significant issue is with using Update(). When you have detached entities being passed around there is a risk that data you don't intend to be updated could be updated. Update will replace all columns, where typically if a client might only be expected to update a subset of them. EF can be configured to check row versions or timestamps on entities against the database before updating when your database is set up to support them (Such as Snapshot isolation) which can help guard against stale overwrites, but still allow unexpected tampering.
As you've already figured out, the better approach is to avoid passing detached entities around, and instead use dedicated DTOs. This avoids potential confusion about what objects represent view/consumer state vs. data state. By explicitly copying the values across from the DTO to the entity, or configuring a mapper to copy supported values, you also protect your system from unexpected tampering and potential stale overwrites. One consideration with this approach is that you should guard updates to avoid unconditionally overwriting data with potentially stale data by ensuring your Entity and DTO have a RowVersion/Timestamp to compare. Before copying from DTO to the freshly loaded Entity, compare the version, if it matches then nothing has changed in the data row since you fetched and composed your DTO. If it has changed, that means someone else has updated the underlying data row since the DTO was read, so your modifications are against stale data. From there, take an appropriate action such as discard changes, overwrite changes, merge the changes, log the fact, etc.
Just alter properties of target and call SaveChanges() - remove the Update call. I'd say the typical use case these days is for the input thing to not actually be a Thing but to be a ThingViewModel, ThingDto or some other variation on a theme of "an object that carries enough data to identify and update a Thing but isn't actually a DB entity". To that extent, if the notion of updating properties of Thing from ThingViewModel by hand bores you, you can look at a mapper (AutoMapper is probably the most well known but there are many others) to do the copying for you, or even set you up with a new Thing if you decide to turn this method into an Upsert

EF attaching object to context in foreach working only first time

I have a list of objects that I need to attach to the Context in order to track changes and, afterward, saving them, but the foreach iterating the items executes only the first time, after that the method ends.
I'm certain that those items already exist in the database.
I have tried both calling the .Attach() method and setting the Entry state to Unchanged.
protected override Task SetViewModelPropertiesAsync()
{
SelectedItems.ForEach(l =>
{
//Context.Pap_Pedido_ODP.Attach(l);
Context.Entry(l).State = System.Data.Entity.EntityState.Unchanged;
// After the first iteration the method ends
});
return base.SetViewModelPropertiesAsync();
}
I expect all the items to be added to context but after the first iteration of the foreach loop breaks the method and continues to the next one, without even giving an exception.
EDIT:
There is more code after the foreach that is being skipped when I do either the Attach or EntityState.
If I comment both the code executes correctly
The behaviour does sound like an exception is being thrown. This is IMO a huge red-flag about List<T>.ForEach() and the main reason I never use it. If you alter your code to:
foreach(var item in SelectedItems)
{
Context.Pap_Pedido_ODP.Attach(item);
Context.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
}
... you should at least now see the exception(s) that are blocking your code. Attaching/Detaching entities between contexts is messy and there are very, very few scenarios where I personally can ever justify it. You are dealing with references to an entity. This means that:
item must not already be associated to any other context.
Context must not already have another entity tracked with the same PK as item.
Point #1 will hinder you because any code returning an entity that "might" want to attach that entity to another context will need to detach, or otherwise load that entity AsNoTracking. Passing a tracked entity to your method from somewhere will break your code.
Point #2 will hinder you because even if the entity passed is detached, if your context happens to already know about that entity via another reference, you have to essentially discard that untracked entity, and use the reference to the tracked instance. This means before attaching any entity you need to check Context .Local for a matching entity.
Only if the entity isn't tracked, and the context does not have a tracked entity with the same PK can you attach it.
If your code is not breaking on an exception and you are debugging, make sure your debug exception handling is set to break on all exceptions. Alternatively you can pop a try/catch block with a breakpoint in the catch to inspect the exception.
Edit: To check instances
foreach(var item in SelectedItems)
{
if(Context.Pap_Pedido_ODP.Local.Contains(item))
{ // This exact instance is already associated to the Context.
// We shouldn't need to copy anything across or do anything...
}
else
{
var existingItem = Context.Pap_Pedido_ODP.Local.SingleOrDefault(x => x.Id == item.Id);
if(existingItem != null)
{ // A different instance matching this one already exists in the context,
// Here if item represents changes we would need to copy changes across to existingItem...
}
else
{ // Item is not associated, safe to attach.
Context.Pap_Pedido_ODP.Attach(item);
// ...
}
}
}
Now it doesn't end there. If "item" contains references to other entities, each and every one will be updated automatically. This can cause problems if some of them have already been associated to the context. This can be caused when the DbContext is too long-lived or where multiple copies of the same instance of a referenced entity are passed back. For instance if I have a set of Orders being saved, and Orders contain references to Customers. 2 orders have a reference to the same customer. When I attach Order #1 to Customer #1, Customer 1 is now associated to the context. When I try to attach Order #2, the instance of Customer #1 is a different instance to Order #1 so attaching Order #2 will generate an error. When dealing with detached entities, you need to take steps to ensure that all instances of objects in the graph that refer to the same record are using the same object instance reference. When you loaded the data from EF, these would be the same object reference, but if you feed them to a Serializer/Deserializer you will get 2 identical copies as separate references. You cannot simply re-attach those object references.
Unfortunately there's no really simple answer I can offer to make it easier, but this is why serializing and deserializing entities can be a terrible idea, and even detaching/attaching instances can be a pain.

Entity Framework - force commit without transaction

I have a loop that looks a bit like this:
CountsToReport = rep.Counts_Get().Where(x => x.Status == "Completed");
foreach (var count in CountsToReport.ToList())
{
//do some stuff
//send an email
count.Status = "Reported";
rep.SaveChanges();
}
Where "rep" is a repository wrapper around an EF context.
When this runs, the unfortunate email recipient gets deluged with spam because the SaveChanges call doesn't actually commit the changes - so the loop keep getting the same counts, emailing them, and marking them as "Reported" but doesn't actually save the change.
If you stop the loop, and re-start the code, the change saves successfully. You can confirm this scenario by stepping through the code: the EF object in C# changes its Status, but the underlying data in SQL doesn't change.
I'm presuming this is because SaveChanges doesn't actually commit the transaction - it just marks the data as having changed ready for the end of the transaction. But we're not using transactions anywhere else in the DB, and it'd be a bit of a pain to change the repository for this one use case.
Is there any other way I can force EF to commit this change and escape my endless loop of doom? Or am I mistaken about the cause?
EDIT: Putting this in the repository and calling it instead of SaveChanges his fixes it:
public void SaveWithTransaction()
{
using (var transaction = new System.Transactions.TransactionScope())
{
db.SaveChanges();
transaction.Complete();
}
}
But it seems ugly. Still interested to know if there's another way round.
EDIT: This is deceptive. It looks like it's just the old add/modified problem again. Marking the object as modified seems to help.
It is quite possible that entities in your CountsToReport are detached from context.
Thus not only ChangeTracker doesn't see any sort of change done over Count, it doesn't know anything about the entity at all.
To have the issue fixed:
iterate through collection of CountsToReport
attach each entity back to context, something like db.Counts.Attach(count)
modify the count.Status and call db.SaveChanges();
like this:
foreach (var count in CountsToReport.ToList())
{
//do some stuff
//send an email
rep.Counts.Attach(count);
count.Status = "Reported";
rep.SaveChanges();
}
Have a look at the following link for more information about entity states and how to deal with them:
http://msdn.microsoft.com/en-us/data/jj592676.aspx

Why doesn't ObjectContext.DetectChanges reset State to Unchanged?

I'm having a little trouble understanding what DetectChanges does in this code (using EF 4.3):
using (var context =new BreakAwayContext())
{
var f = context.Destinations.First();
Console.WriteLine(context.Entry(f).State);
f.Name = "something";
Console.WriteLine(context.Entry(f).State);
context.Entry(f).Property(x => x.Name).CurrentValue =
context.Entry(f).Property(x => x.Name).OriginalValue;
context.ChangeTracker.DetectChanges();
Console.WriteLine(context.Entry(f).State);
}
What I see is
Unchanged
Modified
Modified
Since I reset the value of Name to its original value, why doesn't DetectChanges realize that all the property values match the snapshot of original values and set State back to Unchanged ?
(I realize calling DetectChanges here may be redundant because I am accessing an Entry prior to calling it, but I wanted to see if it would fix State).
The state could have been set manually to Modified, for example:
var f = new Destination { Name = "something" };
context.Entry(f).State = EntityState.Modified; // attaches to context implicitly
context.ChangeTracker.DetectChanges();
EF will detect here that OriginalValue == CurrentValue. But if it would set the state to Unchanged because of that equality it would defeat the purpose of forcing the entity into state Modified - for whatever reason the developer wants that.
EF would have to track the whole history of changes how it came to state Modified to decide if it can safely reset the state to Unchanged or not. In your simple example it would be probably right to reset the state, but in the more general case... who knows. Maybe such a tracking of all changes (instead of only original and current state) is just too complex for a safe solution, so nobody did implement it.
Just a guess...

Help me to understand MY`Using` and `DataContext`

Can someone please explain the below to me. First is how I call the method and the second bit is the LINQ Method.
My curiousity stems from the fact that I get a context error if I un-comment the using portion.
Why? I apparently do not fully understand using and context's. And I would like to better understand this.
Guid workerID = new Guid(new ConnectDAL.DAL.Security().GetUserIDByUserLogin(HUD.CurrentUser));
var myMembers = BLLCmo.GetAllMembers(workerID);
if (myMembers.Rows.Count != 0)
{
dgvMyMembers.DataSource = myMembers;
}
else
{
var allMembers = BLLCmo.GetAllMembers();
dgvMyMembers.DataSource = allMembers;
}
internal static CmoDataContext context = new CmoDataContext();
public static DataTable GetAllMembers()
{
DataTable dataTable;
//using (context)
//{
var AllEnrollees = from enrollment in context.tblCMOEnrollments
select new
{
enrollment.ADRCReferralID,
enrollment.ClientID,
enrollment.CMONurseID,
enrollment.CMOSocialWorkerID,
enrollment.DisenrollmentDate,
enrollment.DisenrollmentReasonID,
enrollment.EconomicSupportWorkerID,
enrollment.EnrollmentDate
};
dataTable = AllEnrollees.CopyLinqToDataTable();
//}
return dataTable;
}
"using" blocks automatically dispose of the object you're using. Since you didn't give further details on what the exact error is, I'm betting its related to the fact that the "using" will dispose of your "context", and then later you'll try to use your context again.
Data Contexts should be used atomically. They're already internally coded to be efficient that way, there's usually no justifiable reason to have one as long-running as you do. The reason you see most samples that use a "using" is because they have the data context initialized immediately before the using (or in it) and then don't try to referenced the disposed context.
As a final note, disposing of objects causes them to release all their internal memory references (such as open connections, cached data, etc).
//Our context exists right now ... unless we've already called this method since the app started ;)
var myMembers = BLLCmo.GetAllMembers(workerID); // Context is disposed at the end of this call
if (myMembers.Rows.Count != 0)
{
dgvMyMembers.DataSource = myMembers; //No prob, we didn't call our function again
}
else
{
var allMembers = BLLCmo.GetAllMembers(); // Oops, our context was disposed of earlier
dgvMyMembers.DataSource = allMembers;
}
You get an error if you use using because the context is disposed the second time it's called by GetAllMembers().
If you need to dispose the context I sugest you create one on the fly in the GetAllMembers() as opposed to having a static context.
Check out the documentation of IDisposable and using.
Here's a link to an article that might help you with Lifetime Management of DataContext.
I have had this problem and did not understand it either at that time. I just removed the using and it worked. The problem was Lazy Loading. The DataContext gave me an entity, but later I tried accessing a property of a parent entity (in the sense of a Foreign Key). Because this parent entity was not loaded the first time, it tried to get it but the DataContext was gone. So I used a DataLoadOptions. If I knew I needed a related entity, I loaded it with the original entity.
Ex: You ask for an invoice to your datacontext, but later you want to access the client's name, like in invoice.Client.Name. The client has not been loaded, so the name is not available.
DataLoadOptions are also important for performance, if you need this related entity in a loop, you'll go back to the DB as many times as you loop if you do not pre-load the child (or parent) entity.

Categories