Click here to Skip to main content
15,889,315 members
Articles / Programming Languages / SQL
Tip/Trick

How to Attach object to a different data context

Rate me:
Please Sign up or sign in to vote.
4.20/5 (3 votes)
10 Feb 2010CPOL2 min read 37.7K   2   7
For the last couple of days, working on my project for my studies I have struggled with this annoying issue regarding LINQ to SQL classes.When we update an entity within a DataContext, we can simple use something likecontext.Entity.Attach(updatedEntityInstance)The proplem is that...
For the last couple of days, working on my project for my studies I have struggled with this annoying issue regarding LINQ to SQL classes.

When we update an entity within a DataContext, we can simple use something like

context.Entity.Attach(updatedEntityInstance)


The proplem is that there is a catch to how my project works. I create my Entity instance within a DataContext, persist it across forms and then update my instance from another DataContext.

Simple enough, same procedure! Right? Wrong…
The Exception “ 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” is thrown…

The problem is (Strange in my opinion that it is a problem) that we are trying to update an instance which came from a disconnected DataContext.

Now there are workarounds I have read up about:

  1. Use 1 instance of DataContext and persist throughout. This is firstly tedious and gave me issues regarding data "caching". An item in the Db is updated, but it does not reflect the change when we retrieve the items.
  2. Right click on a table (Design View) in your dbml file, View code this then creates a partial class of that entity. This is great especially for custom validation of a specific class. In the partial class write a “Detach” method in which we set all the fk properties to their default. After this the Attach Method works. Schweeet! Not really no, I have about 16 classes in my project -> i.e. I must write 16 Detach methods!! and in all of then reset the fk property’s default. My Database schema keeps changing as well so this is super annoying!!!!
  3. I took on the task to do this in reflection, took me for - wait for it - ever but db changes do not affect me now; I only have 1 global method and “MWA… It works!


Method:
public void GenericDetach<T>(T entity) where T : class
{
    foreach (PropertyInfo pi in entity.GetType().GetProperties())
    {
        if (pi.GetCustomAttributes(typeof(System.Data.Linq.Mapping.AssociationAttribute), false).Length > 0)
        {
            // Property is associated to another entity
            Type propType = pi.PropertyType;
            // Invoke Empty contructor (set to default value)
            ConstructorInfo ci = propType.GetConstructor(new Type[0]);
            pi.SetValue(entity, ci.Invoke(null), null);
        }
    }
}

Call it as follows:
this.GenericDetach<Room>(updateRoomInstance);
db.Rooms.Attach(updateRoomInstance);

// Save room details
updateRoomInstance.RoomDescription = txtRoomDescription.Text.Trim();
…
db.SubmitChanges();

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
South Africa South Africa
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.
This is a Organisation (No members)


Comments and Discussions

 
GeneralGood solution but the foreign key value gets disappered. We... Pin
Dharini g13-Jun-10 23:34
Dharini g13-Jun-10 23:34 
GeneralNeed to use the foreign key value after detaching Pin
Dharini g13-Jun-10 23:39
Dharini g13-Jun-10 23:39 
GeneralLife changing stuff right here Pin
prof3ssor10-Feb-10 0:28
prof3ssor10-Feb-10 0:28 
GeneralRe: Life changing stuff right here Pin
Programm3r10-Feb-10 18:31
Programm3r10-Feb-10 18:31 
GeneralRe: Life changing stuff right here Pin
Dharini g13-Jun-10 23:38
Dharini g13-Jun-10 23:38 
GeneralRe: Life changing stuff right here Pin
Dev_258014-Jun-10 0:40
Dev_258014-Jun-10 0:40 
Try to change the DetachRelatedModels method to look as follows:
public void DetachRelatedModels<T>(T entity, List<KeyValuePair<object, object>> values) where T : class
{
    Type enitityType = entity.GetType();
    foreach (PropertyInfo pi in enitityType.GetProperties())
    {
        if (pi.GetCustomAttributes(typeof(AssociationAttribute), false).Length > 0)
        {
            // Property is a foreign key
            Type propType = pi.PropertyType;
            try
            {
                KeyValuePair<object, object> kvp = values.Single(k => k.Key.Equals(pi.Name));
                pi.SetValue(enitityType, kvp.Value, null);
            }
            catch
            {
                // Invoke Empty contructor (set to default value)
                ConstructorInfo ci = propType.GetConstructor(new Type[0]);
                pi.SetValue(entity, ci.Invoke(null), null);
            }
        }
    }
}


You would call the method as follows:

List<KeyValuePair<object, object>> foreignKeys = new List<KeyValuePair<object, object>>()
{
    new KeyValuePair<object,object>("Guest", roomInstance.Guest),
    new KeyValuePair<object,object>("RoomType", roomInstance.RoomType)
};

this.GenericDetach<Room>(updateRoomInstance, foreignKeys);


Hope this helps. If all this is not working for you, the last solution could be to take your current context instance e.g. roomInstance and make another call to the database, loading a "new" instance from the current context and then not detaching is required.

i.e. Let's say that you have an instance of Room called "roomInstance", which is loaded from another context.
Now you pass this instance somewhere where you would like to update "roomInstance" with new values.

So you instantiate a new DataContext instance, get a new room instance according to "roomInstance"'s ID (another db call) and then update its properties before submitting changes:

DataClassesDataContext db = new DataClassesDataContext();
Room updateRoomInstance = db.Rooms.SingleOrDefault(room => room.ID.Equals(roomInstance.ID));
updateRoomInstance.RoomDescription = txtRoomDescription.Text.Trim();
db.SubmitChanges();


Hope this helps...
GeneralGood stuff Pin
Programm3r9-Feb-10 23:43
Programm3r9-Feb-10 23:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.