Wednesday 21 November 2012

Don't modify an Entity that's under Enumeration

Ran into a problem today with Entity Framework complaining that I was modifying an Entity that was under enumeration elsewhere in code. It's common knowledge that modifying a collection that's being enumerated isn't a good idea, but the EF exception that came out didn't make it very obvious:

New transaction is not allowed because there are other threads running in the session.

The culprit was (class names have been anonymised):

foreach (var other in context.Widgets.Where(widget => widget.Box_ID == ID))
{
  // do something with each Widget
}


What the code should have been was:

foreach (var other in context.Widgets.Where(widget => widget.Box_ID == ID).ToList())
{
  // do something with each Widget
}


EF needs to be asked to fetch the whole result set before starting the enumeration: The IQueryable<T> needs to be converted to an IEnumerable<T>. To do this, call ToList() or ToArray() on the IQueryable<T>. After making this change, the exception won't occur any more!

I think this is occurring because there's a transaction enumerating the result of a query, and another transaction which is modifying an entity. These two transactions eventually collide: In the first transaction, rows are fetched and converted into Entities, and these Entities are tracked by EF. Later on, EF loads one of these tracked entities again and the calling code tries to make some changes to it. As EF is already tracking the entity from the first transaction, you get this exception because it exists in two places and their contents is inconsistent.

At the end of the day, this is the old "don't modify things under enumeration" problem, wrapped up in a database :)

No comments:

Post a Comment