views:

2645

answers:

6

In the following code doesn't work as

public void Foo()
{
   CompanyDataContext db = new CompanyDataContext();
   Client client = (select c from db.Clients ....).Single();
   Bar(client);
}

public void Bar(Client client)
{
   CompanyDataContext db = new CompanyDataContext();
   db.Client.Attach(client);
   client.SomeValue = "foo";
   db.SubmitChanges();
}

This doens't work, I get error msg. "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."

How do you work with DataContexts throughout an application so you don't need to pass around a reference?

What

A: 

I've created data access classes that encapsulate all the communication with Linq2Sql. These classes have their own datacontext that they use on their objects.

public class ClientDataLogic
{
    private DataContext _db = new DataContext();

    public Client GetClient(int id) 
    { 
        return _db.Clients.SingleOrDefault(c => c.Id == id); 
    }

    public void SaveClient(Client c) 
    { 
        if (ChangeSetOnlyIncludesClient(c))
            _db.SubmitChanges(); 
    }
}

Ofcourse you will need to keep this object instantiated as long as you need the objects.

Checking if only the rigth object has been changed is altso somewhat bothersom, you could make methods like

void ChangeClientValue(int clientId, int value);

but that can become a lot of code.

Attaching and detaching is a somewhat missing feature from Linq2Sql, if you need to use that a lot, you sould probably use Linq2Entities.

AndreasN
+1  A: 

Yep. That's how it works.

You have tagged this asp.net so I guess it's a web app. Maybe you want one datacontext per request?

http://blogs.vertigo.com/personal/keithc/Blog/archive/2007/06/28/linq-to-sql-and-the-quote-request-scoped-datacontext-quote-pattern.aspx

(P.S. It's a lot harder in WinForms!)

Christopher Edwards
+1  A: 

They really mean it with 'This is not supported.'. Attaching to an object fetched from another data context is not implemented.

There are a number of workarounds to the problem, the recommended way is by serializing objects, however this is not easy nor a clean approach.

The most simple approach I found is to use a readonly DataContext for fetching objects like this:

        MyDataContext dataContext = new MyDataContext() 
        { 
            DeferredLoadingEnabled = false, 
            ObjectTrackingEnabled = false 
        };

The objects obtained from this context can be attached to another context but only applies to some scenarios.

Aleris
+1  A: 

You need to handle object versioning.

An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.

So, if there's no timestamp member or other 'versioning' mechanism provided there's no way for LINQ to determine whether that data has changed - hence the error you are seeing.

I resolved this issue by adding a timestamp column to my tables but there are other ways around it. Rick Strahl has written some decent articles about exactly this issue.

Also, see this and this for a bit more info.

flesh
A: 

I took a look at this and found that it appears to work fine as long as the original DataContext has been disposed.

Try wrapping the DataContext with using() and make sure your changes occur after you've attached to the second DataContext? It worked for me..

        public static void CreateEntity()
        {
            User user = null;
            using (DataClassesDataContext dc = new DataClassesDataContext())
            {
                user = (from u in dc.Users
                        select u).FirstOrDefault();               
            }
            UpdateObject(user);
        }

        public static void UpdateObject(User user)
        {
            using (DataClassesDataContext dc = new DataClassesDataContext())
            {
                dc.Users.Attach(user);
                user.LastName = "Test B";
                dc.SubmitChanges();
            }
        }
RobS
how would that support a tiered architecture where you pass the object up to a logic or UI layer which makes the changes and then passes the object back down?
flesh
That's the problem. Unless you implement object versioning, the only other option is you can query for an object in one context and edit it in another (per my example) but it does not sem possible to support a tiered architecture (use ADO EF instead?).
RobS
Ive just this week dropped EF because it was so difficult to work with - it feels like an early beta rather than a version 1 release - things that should 'just work' don't. Which is a shame because I was looking for a ramped up version of L2S and I wanted to like it so much :)
flesh
Yes, and I imagine you are not alone. Hopefully V2 is much improved. I can't understand why they did not consider n-Tier design and dosconnected states in the first place!
RobS
+3  A: 

The PLINQO framework generates detach for all entities making it easy to detach and reattach objects without receiving that error.

public void Foo()
{
   CompanyDataContext db = new CompanyDataContext();
   Client client = (select c from db.Clients ....).Single();
   // makes it possible to call detach here
   client.Detach();
   Bar(client);
}

public void Bar(Client client)
{
   CompanyDataContext db = new CompanyDataContext();
   db.Client.Attach(client);
   client.SomeValue = "foo";
   db.SubmitChanges();
}

Here is the article that describing how the detach was implemented. http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx

Shannon Davidson