Entity Framework 4 - AddObject vs Attach
Asked Answered
F

5

135

I have been working with Entity Framework 4 recently, and am slightly confused as to when to use ObjectSet.Attach, and ObjectSet.AddObject.

From my understanding:

  • Use "Attach" when an Entity already exists in the system
  • Use "AddObject" when creating a brand new Entity

So, if i'm creating a new Person, i do this.

var ctx = new MyEntities();
var newPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.AddObject(newPerson);
ctx.SaveChanges();

If i'm modifying an existing Person, i do this:

var ctx = new MyEntities();
var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
existingPerson.Name = "Joe Briggs";
ctx.SaveChanges();

Keep in mind, this is a very simple example. In reality i am using Pure POCO's (no code generation), Repository pattern (don't deal with ctx.Persons), and Unit of Work (don't deal with ctx.SaveChanges). But "under the covers", the above is what happens in my implementation.

Now, my question - I am yet to find a scenario where i have had to use Attach.

What am i missing here? When do we need to use Attach?

EDIT

Just to clarify, i'm looking for examples of when to use Attach over AddObject (or vice-versa).

EDIT 2

The below answer is correct (which i accepted), but thought i'd add another example where Attach would be useful.

In my above example for modifying an existing Person, two queries are actually being executed.

One to retrieve the Person (.SingleOrDefault), and another to perform the UPDATE (.SaveChanges).

If (for some reason), i already knew that "Joe Bloggs" existed in the system, why do an extra query to get him first? I could do this:

var ctx = new MyEntities();
var existingPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.Attach(existingPerson);
ctx.SaveChanges();

This will result in just an UPDATE statement being executed.

Factorage answered 13/10, 2010 at 0:59 Comment(1)
Attach is also used in MVC now a days when Putting models back directly to EF. Works pretty well and saves a ton of code lines.Nombril
S
166

ObjectContext.AddObject and ObjectSet.AddObject:
The AddObject method is for adding newly created objects that do not exist in the database. The entity will get an automatically generated temporary EntityKey and its EntityState will be set to Added. When SaveChanges is called, it will be clear to the EF that this entity needs to be inserted into the database.

ObjectContext.Attach and ObjectSet.Attach:
On the other hand, Attach is used for entities that already exist in the database. Rather than setting the EntityState to Added, Attach results in an Unchanged EntityState, which means it has not changed since it was attached to the context. Objects that you are attaching are assumed to exist in the database. If you modify the objects after they’ve been attached, when you call SaveChanges the value of the EntityKey is used to update (or delete) the appropriate row by finding its matching ID in the db table.

Furthermore, using the Attach method, you can define relationships between entities that already exist in the ObjectContext but that have not been connected automatically. Basically the main purpose of Attach, is to connect entities that are already attached to the ObjectContext and are not new so you cannot use Attach to attach entities whose EntityState is Added. You have to use Add() in this case.

For example, let's assume your Person entity has a navigation property named Addresses which is a collection of Address entity. Let's say you have read both Objects from context, but they are not related to each other and you want to make it so:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.PersonReference.Attach(existingPerson)
ctx.SaveChanges();
Syconium answered 13/10, 2010 at 1:27 Comment(12)
Thanks for the answer, i do understand the definition of the two (aka first two paragraphs). But i dont understand a scenario where i NEED to use Attach. Your last paragraph doesnt really make sense to me (reads basically like a combination of the first two paragraphs), can you give me an example of where i would use "Attach" in my above scenario? That's really what im looking for - examples, not definitions. Really appreciate your time though. :)Factorage
No problem, I've added a code snippet to clarify the last paragraph, as you can see we have 2 unrelated objects and Attach help us to relate them to each other. The other example would be to use Attach() method to Attach a "Detached entity" back to context (There are various reasons that you might want a Detached entity to be to Attached back to the context)Syconium
Yep, i gotcha now. I just watched a TechEd vid on EF4 (by Julie Lerman), which showed an example. You might have an entity which you DID NOT retrieve from a query (ie it's diconnected), but you know it exists, therefore you use Attach to perform an UPDATE on that entity. Makes sense, although i still strugle to envison a scenario where you would have a "disconnected" entity. Thanks for your help.Factorage
Great. Can I ask you please to share the link of the video for other fellow developers that might happen to read this post?Syconium
WOW, just had a quick peek at them, that was GREAT!! Dude, you just made my day by that, Thanks so much!!Syconium
Mate, i've watched about 5 of them already. Brilliant stuff (also you should watch the MIX10 videos if you havnt already). The good thing is they are all first-class technical advisors. People like Diego (EF/OData fame), Julie (EF), etc. So i hold their thoughts in high regard.Factorage
Yea, or I found a few videos on architecture from Juval Lowy, which was awesome. They are going to fill my evenings for the rest of the month, that's for sure! BTW, what's the MIX10 videos?Syconium
Are you sure it's != in second line instead of == ?Merci
@Akash; Yes, because we want to find an address that doesn't belong to the existingPerson so that we can add it to that person.Syconium
Well this is even possible while using Add instead of Attach, what goes wrong if I use Add?Merci
If you use Add for an existingPerson in this scenario then you'll get an exception because when the existingPerson is already attached to an ObjectContext instance, the Add method also adds the object to the ObjectContext and this operation is translated into an insert operation in the data source when SaveChanges is called. Read the documentation for Add Method: msdn.microsoft.com/en-us/library/bb351713.aspx and compare it to Attach: msdn.microsoft.com/en-us/library/bb896408.aspxSyconium
The link above shared by RPM1984 is broken, it is redirected now to channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205. EnjoyAnticipation
P
33

This is a late response but it might help others that find this.

Basically, a "disconnected" entity can happen when you manipulate an entity outside of the "using" scope.

Employee e = null;

using (var ctx = new MyModelContainer())
{
     e = ctx.Employees.SingleOrDefault(emp => emp .....);
}

using (var ctx2 = new MyModelContainer())
{
     e; // This entity instance is disconnected from ctx2
}

If you enter another "using" scope then the "e" variable will be disconnected because it belongs to the previous "using" scope and since the previous "using" scope is destroyed then "e" is disconnected.

That's how I understand it.

Psychomancy answered 1/8, 2012 at 18:3 Comment(1)
Tchi's example is an excellent and simple example - yeah, the Employee variable should be declared outside. try e.Address.Street outside the scope and see a null reference exception pop-up. If you Attach then the application will not have to go back to the DB for the Employee in the second scope.Dugout
L
10

This is a quote from Programming Entity Framework: DbContext

Calling Remove on an entity that isn’t tracked by the context will cause an InvalidOperationException to be thrown. The Entity Framework throws this exception because it isn’t clear whether the entity you are trying to remove is an existing entity that should be marked for deletion or a new entity that should just be ignored. For this reason, we can’t use just Remove to mark a disconnected entity as Deleted; we need to Attach it first.

private static void TestDeleteDestination()
{
    Destination canyon;
    using (var context = new BreakAwayContext())
    {
        canyon = (from d in context.Destinations
        where d.Name == "Grand Canyon"
        select d).Single();
    }
    DeleteDestination(canyon);
}
private static void DeleteDestination(Destination destination)
{
    using (var context = new BreakAwayContext())
    {
        context.Destinations.Attach(destination);
        context.Destinations.Remove(destination);
        context.SaveChanges();
    }
}

The TestDeleteDestination method simulates a client application fetching an existing Destination from the server and then passing it to the DeleteDestination method on the server. The DeleteDestination method uses the Attach method to let the context know that it’s an existing Destination. Then the Remove method is used to register the existing Destination for deletion

Laster answered 30/7, 2014 at 3:19 Comment(0)
R
-1

I used this method

var user = _context.Users.Attach(new User
     {
     Name = "Fahimeh",
     Email = "[email protected]",
});
 _context.SaveChanges();

 return View(user);
Rome answered 25/3, 2021 at 12:21 Comment(0)
S
-8

What about only refering to the primary key instead of attaching?

i.e:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson);
ctx.SaveChanges();
Sixpence answered 19/7, 2011 at 12:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.