Solved

LINQ Attach Error

Posted on 2008-10-16
8
836 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
MIM Survival Guide for Service Desk Managers

Major incidents can send mastered service desk processes into disorder. Systems and tools produce the data needed to resolve these incidents, but your challenge is getting that information to the right people fast. Check out the Survival Guide and begin bringing order to chaos.

 

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

Revamp Your Training Process

Drastically shorten your training time with WalkMe's advanced online training solution that Guides your trainees to action.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Recently while returning home from work my wife (another .NET developer) was murmuring something. On further poking she said that she has been assigned a task where she has to serialize and deserialize objects and she is afraid of serialization. Wha…
This document covers how to connect to SQL Server and browse its contents.  It is meant for those new to Visual Studio and/or working with Microsoft SQL Server.  It is not a guide to building SQL Server database connections in your code.  This is mo…
In this brief tutorial Pawel from AdRem Software explains how you can quickly find out which services are running on your network, or what are the IP addresses of servers responsible for each service. Software used is freeware NetCrunch Tools (https…
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…

705 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