Identity Map Pattern and the Entity Framework
This is going to be the seventh post of a series of posts regarding ASP.Net and the Entity Framework and how we can use Entity Framework to access our datastore. You can find the first one here, the second one here and the third one here , the fourth one here, the fifth one here and the sixth one here. I have a post regarding ASP.Net and EntityDataSource. You can read it here.I have 3 more posts on Profiling Entity Framework applications. You can have a look at them here, here and here. In this post I will be looking into the various patterns that Entity Framework implements in order to have a O/R mapper system that functions properly. We will show all these patterns by going through some easy to follow, hands-on examples. There will be a few posts regarding these patterns.In this one we will talk about Identity Map Pattern and how object identity is maintained. I assume that you have access to a version of SQL Server.If you do not, you can download and install the free SQL Server Express edition from here. In this post (step 9) you will find a T-SQL script that will create the database objects of theCompanyEmployees database.Before that you execute the T-SQL script you must create in the Query window the CompanyEmployees database yourself. You can download the companiesemployeesinsertt.zip to insert data into the tables. 1) Launch Visual Studio 2010 (express edition will work fine). Create an empty ASP.Net web site from the available templates and choose a suitable name for it. Choose C# as the development language. 2) Add a new item to your website,a web form.Leave the default name. 3) Add a new project to your solution, a class library project.Remove the class1.cs file from the project. 4) Add a new ADO.Net Entity Data model to the class library project. Choose a suitable name for it, e.g CompanyEmployees.edmx. 5) Then the Wizard pops up. Choose "Generate from Database" option and finish the steps of the wizard by selecting only the tables (Companies,Employees) to be included in the model. Now you have your model ready with all the entities and the generated code. We are ready to leverage this entity data model from our client application. 6) The first pattern I want to explore in EF is the Identity Map Pattern. Entity framework must enforce identity in the same way that databases enforce row identity with the use of primary keys.By that I mean that rows in the database have unique primary key values in the primary key column that distinguish them from any other row in the table.In Entity Framework we have objects in memory representing the rows retrieved from the database.So in our scenario (model) we have in-memory collection of Companies and Employees objects.We would like to have our objects in memory behaving the same way as rows. When we query the data model for the same company entity (the same row in the database) twice, we will not get back 2 distinct objects in memory.Instead we will get the same object reference. 7) In order to demonstrate that we will write some code in the Page_Load event handling routine of the default.aspx page. So inside the routine type,
Company comp1;
Company comp2;
Company comp3;
Company comp4;
using (var context = new CompanyEmployeesEntities())
{
comp1 = context.Companies.Where(comp => comp.CompanyID == 5).First();
comp2 = context.Companies.Where(comp => comp.CompanyID == 5).First();
comp3 = context.Companies.Where(comp => comp.CompanyID == 8).First();
comp4 = context.Companies.Where(comp => comp.CompanyID == 8).First();
Debug.Assert(Object.ReferenceEquals(comp1, comp2));
Debug.Assert(Object.ReferenceEquals(comp3, comp4));
}
8) Run your application. I query the database (twice) for the records with CompanyID=5 and CompanyID=8. I store them in 4 objects and check to see if they have the same object reference or not. If you run the code, you will not get any error(if the condition was false we will get a message box with the call stack).That means we have comp1 and comp2 objects representing the same object/row. The same applies for comp3 and comp4 objects. More about the Debug.Assert method, you can find here. Imagine what would have happened if we had 2 or 3 or even 4 objects in the memory representing the same object. What would have happened when we tried to persist those changes in the database? Who would win?The last one? Would we have an optimistic concurrency exception? That sounds very confusing and believe me it would be a bad situation to be in. Thank God, EF does not work like that. Entity Framework implements the Identity Map Pattern in order to work as we discussed earlier.The main points to keep in mind are:
There is something that is called object cache.It keeps track of all the entities retrieved from the database. In order for this to work there must be a key value in the Entity (CompanyID).So make sure you always have an Entity key defined in the model.
When another query will ask for an entity that has been previously retrieved it will get the same object instance.
Each ObjectContext object has its own object cache.
9) In order to prove this last bullet point, comment out everything you have in the Page_Load event handling routine.
var context = new CompanyEmployeesEntities();
var context1 = new CompanyEmployeesEntities();
var comp1 = context.Companies.Where(comp => comp.CompanyID == 5).First();
var comp2 = context1.Companies.Where(comp => comp.CompanyID == 5).First();
comp2.CompanyName="Another Company";
context1.SaveChanges();
Debug.Assert(comp1.CompanyName != comp2.CompanyName);
Response.Write(comp1.CompanyName);
Response.Write("<br/>");
Response.Write(comp2.CompanyName);
10) Run your application and see the results.You will not get any error message.You will see 2 different company names for the same entity (CompanyID=5). In this example I have created 2 object context objects-context,context1. As you will see the change I make in the second object context does not have any effect on the first one.
The first entity
var comp1 = context.Companies.Where(comp => comp.CompanyID == 5).First();
will not be notified by the changes made to the database from the
var comp2 = context1.Companies.Where(comp => comp.CompanyID == 5).First();
comp2.CompanyName="Another Company";
context1.SaveChanges();
because it will not get the value from the database but from the Object Cache for that Object Context. Hopefully that makes sense.
We only care to have consistency inside in each ObjectContext.
The above scenario will have some problems when it comes to concurrency(many object contexts updating the same entity) but I have explained how to deal with concurrency conflicts in this post.
Email me if you need the source code. Hope it helps!!!