Niels Bosma
Niels Bosma

Reputation: 11528

Linq2Sql: Manage DataContext

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

Upvotes: 4

Views: 6501

Answers (6)

Shannon Davidson
Shannon Davidson

Reputation: 216

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

Upvotes: 4

flesh
flesh

Reputation: 23935

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.

Upvotes: 0

RobS
RobS

Reputation: 9422

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();
            }
        }

Upvotes: 0

Aleris
Aleris

Reputation: 8069

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.

Upvotes: 5

Christopher Edwards
Christopher Edwards

Reputation: 6659

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!)

Upvotes: 1

AndreasN
AndreasN

Reputation: 2897

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.

Upvotes: 0

Related Questions