Entity Framework Object Context Life Cycle compared to Linq to Sql Data Context Life Cycle
Posted on: 2011-09-22
Entity Framework Object Context
The object created from the Entity Model that hold all objects has a life cycle like all other objects. But, this one should catch more your attention because it can come very big with all the object's state tracking that it has to remember. Moreover, it caches values. So, I do not have to tell you that with a medium or big application that the ram memory can come very high.
ObjectContext
class inherit from IDisposable
interface so you can, and should, use USING statement when using Entity Framework. The problem with this approach is that the object tracking is loss. So, it solves only the problem concerning the memory. If you are getting information and know that you gonna change it than is better not to use USING
, but still use .Dispose at the end. But, if you get information only or you update right away the information, it's better to use USING.
States
Entity Framework has 5 possibles states. The first one is when new object is adding. The state is than Added
. Once saved the state of this object come to Unchanged
. When an object is deleted, its state changes to Deleted until the object context saves it to the database. It's than to Detached
. The Modified state is a little bit more tricky. When using Entity Object (Not POCO) when a scalar object is changed, the state automatically change to Modified
. For POCO, it requires to have a call to DetectChanges
to be able to mark the object as Modified
. When an object is loaded from the object context, it's default state is unchanged.
Here is a complete example that show with the Northwind database and Entityframework the state change from the creating to the removing phase.
Lets take this code that create a new Order for a Customer and then remove it.
var db = new NorthwindEntities(); //Add a new Order
var firstCustomer = db.Customers.First();
Debug.WriteLine("---Customer loaded---");
Debug.WriteLine("FirstCustomer:" + db.ObjectStateManager.GetObjectStateEntry(firstCustomer).State);
var newOrder = new Orders(); // db.CreateObject<Orders>(); would do the samething
Debug.WriteLine("---Orders Created---");
Debug.WriteLine("FirstCustomer:" + db.ObjectStateManager.GetObjectStateEntry(firstCustomer).State);
Debug.WriteLine("newOrder: N/A because not yet inside the ObjectContext so not state yet. Could attach before adding to get state but not applicable now because the key is generated by the db and can't attack without key.");
Debug.WriteLine("newOrder:" + db.ObjectStateManager.GetObjectStateEntry(newOrder).State);
firstCustomer.Orders.Add(newOrder);
Debug.WriteLine("---Orders added---");
Debug.WriteLine("FirstCustomer:" + db.ObjectStateManager.GetObjectStateEntry(firstCustomer).State);
Debug.WriteLine("newOrder:" + db.ObjectStateManager.GetObjectStateEntry(newOrder).State); db.SaveChanges();
Debug.WriteLine("---Object Context save---");
Debug.WriteLine("FirstCustomer:" + db.ObjectStateManager.GetObjectStateEntry(firstCustomer).State);
Debug.WriteLine("newOrder:" + db.ObjectStateManager.GetObjectStateEntry(newOrder).State);
Debug.WriteLine("---Remove the created object---");
firstCustomer.Orders.Remove(newOrder);
Debug.WriteLine("FirstCustomer:" + db.ObjectStateManager.GetObjectStateEntry(firstCustomer).State);
Debug.WriteLine("newOrder:" + db.ObjectStateManager.GetObjectStateEntry(newOrder).State);
db.SaveChanges(); Debug.WriteLine("---Object Context save---");
Debug.WriteLine("FirstCustomer:" + db.ObjectStateManager.GetObjectStateEntry(firstCustomer).State);
Debug.WriteLine("newOrder:" + db.ObjectStateManager.GetObjectStateEntry(newOrder).State);
The output of this code is:
---Customer loaded---
FirstCustomer:Unchanged
---Orders Created---
FirstCustomer:Unchanged newOrder: N/A because not yet inside the ObjectContext so not state yet. Could attach before adding to get state
---Orders added---
FirstCustomer:Unchanged newOrder:Added
---Object Context save---
FirstCustomer:Unchanged newOrder:Unchanged
---Remove the created object---
FirstCustomer:Unchanged newOrder:Modified
---Object Context save---
FirstCustomer:Unchanged newOrder:Unchanged
We could also explore the state with the event.
var db = new NorthwindEntities();
db.ObjectStateManager.ObjectStateManagerChanged += new System.ComponentModel.CollectionChangeEventHandler(ObjectStateManager_ObjectStateManagerChanged); //Get existing Customer that has Order
var customersWithOrder = db.Customers.Where(customer => customer.Orders.Count>15); //Add a new Order
var firstCustomer = db.Customers.First();
var newOrder = new Orders(); // db.CreateObject<Orders>(); would do the samething
firstCustomer.Orders.Add(newOrder);
db.SaveChanges();
firstCustomer.Orders.Remove(newOrder);
db.DeleteObject(newOrder);
db.SaveChanges();
This produce:
-->LifeCycle.Customers-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Add
-->LifeCycle.Orders-->Remove
-->LifeCycle.Orders-->Remove
I prefer the first output because it's more clear.
Thread
The ObjectContext class is not tread safe. You must have one object context per thread or to create you own thread synchronization process.
Linq to Sql Data Context
As the ObjectContext, the DataContext class hold the track of each object state. It knows what has been modified, deleted or added. Also, Linq to Sql uses state even if nothing has changed to the object. When loaded, the object will be Unchanged
or if you create a new object or deserialize an object, it will be a Untracked
.
When an object is attached, by default its state will be PossiblyModified
and this is until the SubmitChange
. The next three states are very common ToBeInserted
, ToBeModified
, ToBeDeleted
. The last possible state is when an object has been SumbitChange
when it was ToBeDeleted
. When this happen, the object is still in the DataContext but with the Deleted
state.
Thread
The Linq to Sql DataContext class is not thread safe. It should not be static, neither
Where and when should EF or Linq2Sql live?
Matt Warren, software achitect at Microsoft on the C# programming language product team and member of the team that created LINQ to SQL explains this question with a design pattern. Theses ORM should be treated as the Unit of Work pattern.
A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.
That's it. The ORM object should live until the business task is done. For example, you need to edit a employee, you create the object when loading it, it's alive until it's saved or the task is cancelled. It does not remain until the application close in the memory.