17

I recently started using Entity Framework, and it has been kind of a pain to check if I really need to add new records to the database or not.

If the Entity I need to add to the database is already on it, I will know, because I do a query before inserting it, and if it exists, then I keep that instance because I need to use it in some relationships.

Let's suppose my entity name is Book.

The problem comes when an entity isn't in the database, and I do:

Book b = //...
modelContainer.AddToBooks(b);

I could easily do:

modelContainer.SaveChanges()

everytime I add a new entity (no matter what entity it is), and this will work fine, because as I'm inserting one kind of entry at a time, and checking if it already is in the database, I won't have duplication problems.

But what if I want to avoid calling SaveChanges() so often?

In this question: Is is possible to check if an object is already attached to a data context in Entity Framework?, the author of the question provides a method that kind of helps me in my case, but it does not work if I Add the object to the context instead of Attaching it.

My question (maybe two, but very related) is: What is the difference between Add and Attach and how can I solve my problem?

Edit:
Here is an example of the problem I'm having.

I have an entity Result that has a relationship with two more entities: Trainer and Horse.

I get the data from an external source, so I have to create manually all the entities.

Everytime I need to insert a new Trainer, I do:

var trainer = Trainer.CreateTrainer(Id)

Then I query the database to see if a trainer with that Id is already on the database. If it is, then I replace the trainer variable with the one that is on the database.

If it isn't, I can do two things here:

  • Attach the trainer to the context (I can check if it already exists using the key)
  • Add the trainer to the context (using AddToTrainers(...))

The same process for Horse.

Now, when I need to create a new Result (that contains a Trainer and a Horse), I assign the previous trainer & horse to that result instance.

What should I do here to be able to add to the context that new Result?

  • If I attach the trainer/horse, then when I attach the result, I get InvalidOperationException, teling me that the trainer is already on the object context.
  • If I add the trainer instead of attaching it, I get another error (can't remember it right now, but it was telling me that a Trainer was already on the database).

Important:
The first error is given when attaching the result, and the second one when doing SaveChanges().

What I want to avoid here is calling SaveChanges() everytime I add a new result.

Community
  • 1
  • 1
Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124

2 Answers2

23

ObjectContext internally tracks all entities which was either loaded by context, attached or added. Only these entities can be modified in database when SaveChanges is invoked. Each such entity has a ObjectStateEntry in the ObjectStateManager. One of the main properties of the ObjectStateEntry is a State. The state is of enum type EntityState which offers these values:

  • Added
  • Deleted
  • Detached
  • Modified
  • Unchanged

Each entity loaded from the database is in Unchanged state. Detached is special state. You will not find ObjectStateEntry with Detached state in the ObjectStateManager. But if you ask ObjectStateManager for the ObjectStateEntry for entity not tracked by the context it will create a new ObjectStateEntry with Detached state.

Now the difference between Attach and AddObject:

  • Attach - if you call this method ObjectContext will start tracking whole object graph (main entity and all related entities). All entities which were not tracked yet will be set to Unchanged state.
  • AddObject - if you call this method ObjectContext will also start tracking whole object graph (main entity and all related entities). The difference is that all entities which were not tracked yet will be set to Added state (= new objects which must be set to database).
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • @Ladislav Thank you for your answer. According to my problem, how can I avoid inserting an instance of an entity `A` with a relationship with an instance of `B`, when I already added/attached the instance of `B`? I just want to insert the instance of `A`, but I'm getting `InvalidOperationException` – Oscar Mederos Apr 20 '11 at 08:01
  • Add example to your question. – Ladislav Mrnka Apr 20 '11 at 08:09
  • Lets try to attach / add entities first and once they are connected to context set up their relations. – Ladislav Mrnka Apr 20 '11 at 20:26
  • @Ladislav Hmm.. interesting... I will try adding the `result` to the context, and then set the relations with `trainer` and `horse`, both already in context or in the database. Is that what you meant? – Oscar Mederos Apr 20 '11 at 20:32
  • @Ladislav I gave vote up for both the question and your answer. Good explanation. Now the difference between Attach and Add is clear. I don't see any usage of Attach more often as while loading the entity, we are going to use ObjectContext to load it which will set the State to Unchanged and so it will never be Detached. Can you please describe the usage for Attach function – DotNetInfo May 11 '11 at 06:11
  • @DotNetInfo: If you always load entity before modification you don't need `Attach` but if the modified entity is no loaded (for example entity constructed from data passed in web service or request in web application) you can use `Attach` to modify entity without loading it first. This is generally known as working with detached entities and it is much harder. – Ladislav Mrnka May 11 '11 at 07:00
10

I know that I am a little late to this post, but was looking for similar solution... and then I found this Microsoft article which may succinctly answers most of the OP's questions, and may help future SO viewers?:

Add/Attach and Entity States == ( http://msdn.microsoft.com/en-us/data/jj592676.aspx )

From the article:

This topic will cover how to add and attach entities to a context and how Entity Framework processes these during SaveChanges.

And specifically see the last section on that page:

Insert or update pattern: A common pattern for some applications is to either Add an entity as new (resulting in a database insert) or Attach an entity as existing and mark it as modified (resulting in a database update) depending on the value of the primary key.

J-Rome
  • 155
  • 1
  • 8