Home
Help
Register
Log in

Search

 
   Active Threads  

You are here: Home > LLBLGen Pro > LLBLGen Pro Runtime Framework> Collection enumeration and InvalidOperationException
 

Pages: 1
LLBLGen Pro Runtime Framework
Collection enumeration and InvalidOperationException
Page:1/1 

  Print all messages in this thread  
Poster Message
obzekt
User



Location:

Joined on:
29-Apr-2004 18:18:59
Posted:
49 posts
# Posted on: 04-Jan-2007 22:59:05.  
Hello. Consider a relation Customer and Orders (1:n) and this code:

Code:
foreach (OrderEntity oe in customer.Orders)
{
     oe.SetNewFieldValue((int)OrderFieldIndex.CustomerID, null);
     oe.Save();
}


Now the above used to work fine but now throws InvalidOperationException from MoveNext (Collection was modified; enumeration operation may not execute) with the latest version of LLBL that uses generics, after the first iteration. Is that by design and why that breaking change? What do you suggest to do as workaround?


  Top
bclubb
User



Location:
Norman, Oklahoma
Joined on:
12-Feb-2004 22:18:04
Posted:
934 posts
# Posted on: 05-Jan-2007 02:23:27.  
This is how the enumerators with collections work. Here's a quick link on it http://msdn2.microsoft.com/en-us/library/system.collections.ienumerator.movenext.aspx.

I believe this would work if you use a for loop instead of foreach since it won't use MoveNext.


  Top
obzekt
User



Location:

Joined on:
29-Apr-2004 18:18:59
Posted:
49 posts
# Posted on: 05-Jan-2007 08:17:30.  
The MoveNext exception is correct. What is strange is that the orders collection obtained *before* changing the FK of one of each orders to null, changes afterwards and its Count is decremented. This is new behavior with LLB2 and I want to know if it is by design. In the older version, an entity had to be explicitly removed from the collection, which is more logical in my opinion.
  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37870 posts
# Posted on: 05-Jan-2007 10:30:36.  
It is indeed by design and new behavior in v2.0 (new as in: compared with 1.0.2005.1)

IMHO it's logical what is happening in v2.0, as an order which doesn't reference a given customer object, shouldn't be in an Orders collection of said customer.


Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
obzekt
User



Location:

Joined on:
29-Apr-2004 18:18:59
Posted:
49 posts
# Posted on: 05-Jan-2007 14:44:44.  
My understanding is that the collections were designed as simple read-only containers of data, a snapshot of a query at a given point. That's why an entity is not deleted from the DB when removed from the collection. I remember this was confusing to many users in the past but it was a good thing I believe. Now it seems as if the collections try to be 'smart' and in sync with the DB state of their contained entities which creates ambiguity. It is actually counter-intuitive to delete an entity with FKs that don't cascade, since you can't iterate the collection. You have to do something like:

Code:
int count = customer.Orders.Count;
for (int i = 0; i < count; i++)
{
    OrderEntity oe = customer.Orders[0];
    oe.SetNewFieldValue(OrderFieldIndex.CustomerID, null);
    oe.Save();
}
customer.Delete();


Or maybe there is a more elegant way?
  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37870 posts
# Posted on: 05-Jan-2007 15:22:11.  
obzekt wrote:
My understanding is that the collections were designed as simple read-only containers of data, a snapshot of a query at a given point. That's why an entity is not deleted from the DB when removed from the collection. I remember this was confusing to many users in the past but it was a good thing I believe. Now it seems as if the collections try to be 'smart' and in sync with the DB state of their contained entities which creates ambiguity. It is actually counter-intuitive to delete an entity with FKs that don't cascade, since you can't iterate the collection. You have to do something like:

Code:
int count = customer.Orders.Count;
for (int i = 0; i < count; i++)
{
    OrderEntity oe = customer.Orders[0];
    oe.SetNewFieldValue(OrderFieldIndex.CustomerID, null);
    oe.Save();
}
customer.Delete();


Or maybe there is a more elegant way?

I disagree. The thing is that the SYNC code adds the entity to the collection, so doing:
myOrder.Customer = myCustomer;
adds myOrder to myCustomer.Orders;
If you then do:
myOrder.Customer = null; // or set it to another customer
it's nothing more than logical to remove myOrder from myCustomer.Orders.

We had a lot of requests to change this in the way it is now, so we did.

To overcome this, you can simply do:
List<OrderEntity> toProcess = new List<OrderEntity>();
toProcess.AddRange(customer.Orders);
foreach(OrderEntity o in toProcess)
{
// do your work here;
}


Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
obzekt
User



Location:

Joined on:
29-Apr-2004 18:18:59
Posted:
49 posts
# Posted on: 05-Jan-2007 15:51:14.  
I understand. Now what if you have an OrderCollection of new entities and you want to add them to a customer? Will this work:

Code:
foreach (OrderEntity oe in coll)
    oe.Customer = myCustomer;

or will 'coll' drop the entities after they get added to myCustomer.Orders?

Is that breaking change documented? It seems I missed it, and now need to review lotsa code Sad
  Top
Otis
LLBLGen Pro Team



Location:
The Hague, The Netherlands
Joined on:
17-Aug-2003 18:00:36
Posted:
37870 posts
# Posted on: 05-Jan-2007 18:47:43.  
obzekt wrote:
I understand. Now what if you have an OrderCollection of new entities and you want to add them to a customer? Will this work:

Code:
foreach (OrderEntity oe in coll)
    oe.Customer = myCustomer;

or will 'coll' drop the entities after they get added to myCustomer.Orders?

You can simply do myCustomer.Orders.AddRange(coll);

and the Customer property of the order instances will be set as well.

Quote:

Is that breaking change documented? It seems I missed it, and now need to review lotsa code Sad

In the what's new -> runtime libraries:
Quote:

Synching setup of entities related to eachother has been enhanced, so that also dereferencing through FK field changes will result in proper clean-up of synchronization data and references.

Though that's easily missed.

Rule of thumb: if you are doing foreach over a collection, don't change values in the object you're consuming which you pulled from the collection, in that case either use a for loop or use an intermediate list. I always use an intermediate list if I want to use a foreach statement, as it's 100% correct: a forloop also can give you bad indexes.


Frans Bouma
LLBLGen Pro / ORM Profiler Lead Developer | Blog | Twitter
 
Top
Pages: 1  


Powered by HnD ©2002-2007 Solutions Design
HnD uses LLBLGen Pro

Version: 2.1.12172008 Final.