In two-tier applications, a single DataContext is used for queries and updates. Some of these examples I described in this post. However, for applications with additional tiers (almost all serius applications today), it is necessary to use separate DataContext instances for query and updates. For example, in case of ASP.NET applications, query and update are done for separate requests to the Web server. Hence, it is impractical to use the same DataContext instance across multiple requests. In such cases, a DataContext instance needs to be able to update objects that it has not retrieved. The multi-tier entity support in LINQ to SQL provides such a capability through the Attach() method. This has also been described in this post.
Sometimes you could even have one instance of an entity which results from some query in one DataContext. Now you would like to use another context to do any operation with this instance. This is shown in the next sample:
DalContext dal1 = new DalContext();
TTransponder t = dal1.TTransponders.First();
DalContext dal2 = new DalContext();
DalContext dal2 = new DalContext();
dal2.TItems.InsertOnSubmit(oldItem);
dal2.SubmitChanges();
|
When you execute this you will get following error:
"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."
There are many who have complaining about this. I'm one of them too. For example Rick posted few months ago about importance of detaching of objects from one context. We hoped, that this will be corrected in the release version of Linq2Sql. Unfortunately, this remains as by design as it is. To solve this problem one have tried to implement a kind of Detach() method which would disconnect object from the context. In some cases this work, but in general it doesn't.
This was a reason why I decided to implement my own Detach(). Here is how I do this. My idea is to avoid to write any method in partial classes and to dig in any internal feature of Linq, because it will be changed for sure. So, I decided to use serialization. This allows me to make a copy of the object and lose any trace to the original context. Take a look on following code.
public T Detach<T>(T item) { string detachedProp = Serialize(item); return (T)Deserialize(typeof(T), detachedProp); }
private string serialize(object value) { if (value.GetType() == typeof(string)) return value.ToString();
StringWriter stringWriter = new StringWriter(); using (XmlWriter writer = XmlWriter.Create(stringWriter)) { DataContractSerializer serializer = new DataContractSerializer(value.GetType()); serializer.WriteObject(writer, value); } return stringWriter.ToString(); } private object deserialize(Type type, string serializedValue) { if (type == typeof(string)) return serializedValue;
using (StringReader stringReader = new StringReader(serializedValue)) { using (XmlReader reader = XmlReader.Create(stringReader)) { DataContractSerializer serializer = new DataContractSerializer((type));
object deserializedValue = serializer.ReadObject(reader);
return deserializedValue; } } }
|
Now, detaching of item as as simple as this:
TItem oldItem = oldItems.First();
TItem detachedItem = Detach<TItem>(oldItem); |
Just call Detach<T> by passing of attaches item and the method will return completely new copy of this. About performance related to usage of different serializers take a look on this post.
At the end I would like to recommend following links:
1. Entity staffs and Linq2Sql explanation: http://msdn2.microsoft.com/en-us/library/bb425822.aspx#linqtosql_topic34
2. An intersting research project related to Parallel Linq: http://msdn.microsoft.com/msdnmag/issues/07/10/PLINQ/default.aspx
[Visit: http://www.daenet.eu]
Posted
Jan 20 2008, 08:28 PM
by
Damir Dobric