views:

64

answers:

1

I ran into an issue trying to update a foreign key field:

record.ForeignId = newId;

It bombs with "Operation is not valid due to the current state of the object" due to SQLMetal code that throws System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException().

I was not the first to hit this issue:

http://stackoverflow.com/questions/2058426/linqtosql-error-operation-is-not-valid-due-to-the-current-state-of-the-object and http://social.msdn.microsoft.com/forums/en-US/linqtosql/thread/f9c4a01a-195a-4f2b-a1cb-e2fa06e28b25/ discuss it, amongst others.

Their solution is this:

record.Foreign = Database.Foreigns.Single(c => c.Id == newId);

That, of course causes a DB lookup on Foreign just to get an object back that has the Id I already know! So, how do I accomplish this update without the pointless query (or queries if I have lots of these FKs)?

+1  A: 

You could new up an instance of the parent (with the correct Id), Attach it to the datacontext as the existing record state, then assign the Parent property of your child object.

Here's some code:

int theId = 5;
Parent p = new Parent() { Id = theId};
dc.Parents.Attach(p);
child.Parent = p;
David B
@David B: I didn't know about Attach(), but I tried that and got a duplicate key error: "Cannot add an entity with a key that is already in use." Did it query the DB to find that out, or was that record maybe just hanging out in my data context... ?
Scott Stafford
DataContext tracks all records that it has loaded (by Primary Key). If it has loaded a Parent with Id=5, you will not be able to attach another instance of Parent with Id=5.
David B
@David B: And is there any way to see if one is cached and fish it out if it does? And otherwise gin up a new one like in your answer? Or maybe tell it to drop the one it has first? Or anything to make this scheme work?
Scott Stafford
The only method that allows you to get instances from a DataContext or DataTable without hitting the database is DataContext.GetChangeSet - which won't help if you have no update/insert against the parent object. If all you're doing is setting a FKey to a known value, it might be wise to open the hood and issue a DataContext.ExecuteCommand instead of going through the ORM. Your other choice is to constrict the lifecycle of DataContext until you can track what has/hasn't been loaded.
David B