views:

729

answers:

3

This is a little out there but I have a customer object coming back to my controller. I want to just reconnect this object back to the database, is it even possible? I know there is a datacontext.customers.insertonsubmit(customer), but is there the equivalent datacontext.customers.updateonsubmit(customer)???

A: 

You want to use the attach method on the customers table on the data context.

datacontext.customers.Attach(customer);

to reconnect it to the data context. Then you can use SubmitChanges() to update the values in the database.

EDIT: This only works with entities that have been detached from the original data context through serialization. If you don't mind the extra call to the database, you can use the idiomatic method in ASP.NET MVC of retrieving the object again and applying your changes via UpdateModel or TryUpdateModel as @Odd suggests.

tvanfosson
Last time I was running through this (a couple months ago) I always got an error on this statement. Something about (paraphrasing) "This object was originally attached to another data context" and it can't be attached...Has that changed recently
Jason Punyon
Yeah sorry I ran into an issue with this as well, not the answer it seems.
Al Katawazi
You have to detach it from the old data context. This simply involves serializing and deserializing it. If you don't mind the extra query to the database, you can do as @Odd suggests and requery the model and use UpdateModel (or TryUpdateModel) to apply your changes. This is, in fact, what I do.
tvanfosson
See this blog post: http://blogs.msdn.com/dinesh.kulkarni/archive/2007/10/08/attach-if-you-have-something-detached.aspx
tvanfosson
+3  A: 

The customer that you post from the form will not have entity keys so may not attach well, also you may not have every field of the customer available on the form so all of it's fields may not be set.

I would recommend using the TryUpdateModel method, in your action you'll have to get the customer from the database again and update it with the form's post variables.

public ActionResult MySaveAction(int id, FormCollection form)
{
    Customer updateCustomer = _Repository.GetCustomer(id);

    TryUpdateModel(updateCustomer, "Customer", form);

    _Repository.Save(updateCustomer);
}

You will have to add in all your own exception handling and validation of course, but that's the general idea.

Odd
+5  A: 

This is what I don't like about LINQ-to-SQL.

It generally works fine if you're querying and updating in the same scope, but if you get an object, cache it, and then try to update it later, you can't.

Here's what the documentation says:

Use the Attach methods with entities that have been created in one DataContext, and serialized to a client, and then deserialized back with the intention to perform an update or delete operation. Because the new DataContext has no way of tracking what the original values were for a disconnected entity, the client is responsible for supplying those values. In this version of Attach, the entity is assumed to be in its original value state. After calling this method, you can then update its fields, for example with additional data sent from the client.

Do not try to Attach an entity that has not been detached through serialization. Entities that have not been serialized still maintain associations with deferred loaders that can cause unexpected results if the entity becomes tracked by a second data context.

A little ambiguous IMHO, specifically about exactly what it means by "serialized" and "deserialized".

Also, interestingly enough, here's what it says about the DataContext object:

In general, a DataContext instance is designed to last for one "unit of work" however your application defines that term. A DataContext is lightweight and is not expensive to create. A typical LINQ to SQL application creates DataContext instances at method scope or as a member of short-lived classes that represent a logical set of related database operations.

So, DataContexts are intended to be tightly scoped - and yet to use Attach(), you have to use the same DataContext that queried the object. I'm assuming/hoping we're all completely misunderstanding what Attach() is really intended to be used for.

What I've had to do in situations like this is re-query the object I needed to update to get a fresh copy, and then do the update.

Daniel Schaffer