This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Hi, I am currently evaluating lightspeed and hit upon something strange when deleting entities: Roughly I am doing the following using (TransactionScope scope = new TransactionScope()){ //Get the city with id = 1 City c1 = UnitOfWorkScope.Current.FindOne(1);//remove it UnitOfWorkScope.Current.Remove(c1);UnitOfWorkScope.Current.SaveChanges(); //reload it -> should not exist!! City c2 = UnitOfWorkScope.Current.FindOne(1);}
It would expect object c2 to be null, but it just loads the object (same values as c1) if I use SaveChanges(true), I get a null object, also if i do var c2 = from c in UnitOfWorkScope.Current.Cities where c.Id == 1 select c; it will return null (with just a normal SaveChanges)
Any idea what is causing this behavior?
Best regards Arend Melissant
|
|
|
This is down to when LightSpeed thinks it needs to consult the database, and when it thinks it can get away without doing so. Recall that the unit of work tracks a set of entities. That is, it remembers the entity associated with each ID (actually each type and ID). This is how it wires up foreign key associations, returns the same entity instance from different queries, etc. Whenever LightSpeed needs to provide an entity with a particular ID, whether as part of a to-one association or as the result of a query or for any other reason, it first of all looks in the identity map, and if the identity map contains an entity with the right ID, it returns that. This includes when you do a "find by ID." The first thing LightSpeed does is check to see if the entity with that ID is already registered with the unit of work. If it is, it returns that entity immediately. It thinks there is no point querying the database, because it would just return the existing entity instance. This is what's happening in your first scenario: your second FindOne is against the same unit of work as the first FindOne, so the removed entity is still registered with the unit of work, so LightSpeed finds it as the match for that ID, and returns it immediately without consulting the database. If you do a SaveChanges(true), the unit of work clears its identity map -- that is, it throws out its currently tracked entities. Therefore, when you do the second FindOne, LightSpeed doesn't find an entry in the identity map with the right ID (because the identity map is now empty). So it falls back on querying the database, and because the row has been deleted, finds nothing. If you do the LINQ select (or, equivalently, a Find rather than a FindOne), LightSpeed can't go straight to the ID. You and I can see from looking at the LINQ query that it's equivalent to a "find by ID," but LightSpeed doesn't take that shortcut: it sees a "where" clause, builds a query and ships that off to the database. Again, because the database row has been deleted (and no other rows matches the query), this returns nothing, so you get an empty collection. This is a bit confusing I realise -- I hope it makes more sense now! |
|
|
Ivan, thanks for the explanation. Problem I have that this renders the FindOne useless as I cannot rely on it returning the correct value..... BTW, in the debug window I see an SQL query being rendered for the second FindOne.... Best regards Arend Melissant |
|
|
Not sure why you'd see a SQL query for the second FindOne if the entity is already in the identity map; I'm pretty sure that shouldn't be happening. However, it's probably moot because it's clear FindOne won't serve your purpose. To be honest, I'm not sure why you're using FindOne at all in this scenario. In your sample code you obviously know that there should no longer be a City with ID 1, so the FindOne is superfluous; presumably this isn't the case in your real code. So I'm guessing that you've got code in one place that loads items for Purpose A, and potentially adds and removes items, and saves the changes; then you have code in another place that reload one of those items for Purpose B; and the B code wants to be sure that it's working with an up-to-date version of the data. In that case, you should consider using separate units of work for the two code sections. The A code, which is performing one logical business transaction, should load its items, make its changes, save those changes, and close down its UOW. Then the B code, which is performing a separate business transaction, should start a new UOW. Because this is a new UOW, it won't be confused by the identity map from A, so FindOne will correctly return null. That said, if you want A and B to take place under a single *database* transaction, as your sample code implies, this may get a bit more fiddly. And of course all of the above is just a guess anyway! If you can say a bit more about your actual scenario, we'd be happy to advise. |
|
|
Hi,
Sorry for getting back after so long, busy busy busy You were correct in your last assumption, and I have rewritten parts to solve the problem (still getting to grips with lightspeed I guess :-))
|
|