Solved

LINQ Attach Error

Posted on 2008-10-16
8
826 Views
Last Modified: 2013-12-17
I am attempting to save a record using Windows Forms when a "Save" button is clicked. My understanding is that it is best to use a new "datacontext" and attach the current record to it. I have tried everything I can (and others) can think of but nothing seems to work for me. My code is below. When I run this I get an error that states "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." Any help is greatly appreciated.
private void btnSaveRecord_Click(object sender, EventArgs e)

{

	Quant.QuantDbDataContext db = new Quant.QuantDbDataContext();

	Quant.quantInfo currQi = (Quant.quantInfo)bsQuantInfo.Current;

 

	Quant.QuantDbDataContext db2 = new Quant.QuantDbDataContext();

	db2.DeferredLoadingEnabled = false;

	Quant.quantInfo prevQi = db2.quantInfos.Single(q => q.quantID == currQi.quantID);

	db2 = null;

 

	db.quantInfos.Attach(currQi,prevQi);

	db.SubmitChanges();

						

	btnSaveRecord.Enabled = false;

}

Open in new window

0
Comment
Question by:jfrank79
  • 5
  • 3
8 Comments
 
LVL 4

Expert Comment

by:novynov
ID: 22740712
Updating a "disconnected" object (like with an n-tier application) in Linq has some challenges due to the change tracking mechanisms and the default use of optimistic concurrency. In order to safely update an object, Linq needs to know the original state of an object and be able to detect what has changed. In an Attach()ed object, Linq doesn't have that info without some help. Note: this whole subject of Attach() is somewhat confusing, as evidenced by the chatter on the net.

That said, I notice that you are using LINQ in a Windows forms app. Is it safe to assume that you are retrieving the original object in the same form? If so, is there any reason you wouldn't just have one datacontext exist as a private member of your form? This context would be used for retrieve and update. If so, you wouldn't need to reattach your changed object to a different context - thus avoiding the problem.

If you must use a new datacontext (as happens in the case of a web service using linq), there are a number of approaches to doing updates:

- Add a timestamp field to the table and use the Update(obj, true) overload. This isn't always an option if your schema isn't controlled by you.
- Turn the UpdateCheck property on your column mappings to "Never." Basically, this turns off change tracking, and will "blast" all of the columns into the db. In this case, you call Attach(obj, true). Obviously, this method comes with risk, especially in a multi-user db.
- Maintain a copy of the original/unchanged object, attach it to your new datacontext using the Attach() overload and "play" the changes into it (e.g. origObj.PropName = changedObj.PropName).
- Retrieve the original object from the db with your new datacontext and play your changes into it.
-Detach the object from its original context (I believe this is only possible through serialization or some manual work), make your changes to this object, and then reattach it using Attach(changedObj, originalObj). It appears that you are trying to do something similar to this above. I believe the difference is that the changed object was not detached and the original object was retrieved from the same datacontext that you are updating against. I believe neither of these are supported. I'm still validating the details.

That said, while I still investigate the details on the last one and other possibilities, I thought I'd at least post this with some possible options that may quickly resolve your issue.

Also, here are some additional resources on the subject:

DataRetrieval and CUD Operations in N-tier Applications - http://msdn.microsoft.com/en-us/library/bb546187.aspx


Attach() if you have something detached - http://blogs.msdn.com/dinesh.kulkarni/archive/2007/10/08/attach-if-you-have-something-detached.aspx

How to attach object to a different datacontext - http://msmvps.com/blogs/omar/archive/2007/12/08/linq-to-sql-how-to-attach-object-to-a-different-data-context.aspx

A comment from the MSDN docs on Update() that may be of interest:

"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."

Please let me know if this helps.
0
 
LVL 4

Expert Comment

by:novynov
ID: 22740931
Here's another post, this one regarding your specific error:

http://geekswithblogs.net/michelotti/archive/2007/12/25/117984.aspx

0
 
LVL 4

Expert Comment

by:novynov
ID: 22741987
So, I did some experimentation trying to reproduce your exact scenario...and better understand the behavior of the 2 arg Attach() call.

Interestingly, if you have a single table with no FK relationships represented as associations in your dbml (note: they can still exist in the db - just not in your dbml), the approach you took does work (though it may not be supported - see previous refs). The link in my previous post gives one reason why and offers a solution - basically, a manual detach and re-attach, including "child" entities. However, I tried this in a number of scenarios, and it does not appear to work as advertised, at least not with 3.5 SP1.

Hope this helps. Let me know if you need more info.
0
 

Author Comment

by:jfrank79
ID: 22742179
I have tried most of the above solutions (timestamp, detach, etc...). I went as far as copying the updated record into a completely new detached object but still got the same error. I am curios about the "Update()" call as I have not seen this available. I was under the impression "Attach()" was the only way to update. Would be great if I was wrong.

I need to have a new datacontext because data can be changed outside of the form's datacontext (other users, background processes, etc...). I appreciate your help on this!
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 4

Accepted Solution

by:
novynov earned 500 total points
ID: 22742404
I may be confused about what you're saying, but there is no "Update()" call. My comments above refer to the UpdateCheck property on the column/prop definition in the dbml. Is that what you're referring to?

When you used the timestamp approach, did you use Attach(obj, true) instead of the one that takes changed and original object? It seems like that one should work in the scenario you describe...or at least give a different error.

It also seems like grabbing the latest object in a new datacontext, playing your changes into it, and submitting them should work. Obviously, it would fail if another user/process changed the data "under" you (between get and update)...but the error would be different...and could be managed by trapping the exception, etc.

Let me know how else I can help.

0
 
LVL 4

Expert Comment

by:novynov
ID: 22742510
Something struck me that may help me understand better how to help find a solution. What is the source of the objects in bsQuantInfo.Current? How does it get populated?
 
0
 

Author Comment

by:jfrank79
ID: 22742525
I went the route of grabbing the latest object and putting the changes into the existing object. This worked when I was just changing the top-level. In my code below if I try to update just the quantName it works but if I try to update any child objects it breaks with the same error, so I'm guessing my problem has to do with child objects. I've got most of the code commented out just to focus on which area is breaking. Your help is greatly appreciated.
private void btnSaveRecord_Click(object sender, EventArgs e)

{

        Quant.QuantDbDataContext db = new Quant.QuantDbDataContext();

        Quant.quantInfo currQi = (Quant.quantInfo)bsQuantInfo.Current;

	Quant.quantInfo newQi = db.quantInfos.Single(q => q.quantID == currQi.quantID);

	newQi.quantCatVarLinks = currQi.quantCatVarLinks;

	//newQi.quantDailyDatas = currQi.quantDailyDatas;

	//newQi.quantID = currQi.quantID;

	//newQi.quantMonDatas = currQi.quantMonDatas;

	//newQi.quantMonthlyExposures = currQi.quantMonthlyExposures;

	newQi.quantName = currQi.quantName;

	//newQi.quantOtherIdentifier = currQi.quantOtherIdentifier;

	//newQi.quantPerTracID = currQi.quantPerTracID;

	//newQi.quantStataName = currQi.quantStataName;

	//newQi.quantType = currQi.quantType;

	//newQi.quantVarUpdates = currQi.quantVarUpdates;

	//newQi.quantWeeklyDatas = currQi.quantWeeklyDatas;

	db.SubmitChanges();
 

	btnSaveRecord.Enabled = false;

}

Open in new window

0
 

Author Comment

by:jfrank79
ID: 22742925
I appeared to have solved the problem (or at least found the culprit). It is not all child members that cause the problem, just the one I chose to test. I think I know why and am now finally able to proceed. Thanks for your help!
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now