Identity Map pattern in Entity Framework Core and Difference of Find And First/Single

Identity Map pattern in Entity Framework Core and Difference of Find And First/Single

In the design of DBMS the identity map pattern is a database access design pattern used to improve performance by providing a context-specific, in-memory cache to prevent duplicate retrieval of the same object data from the database.


In Martin Fowler’s book Patterns of Enterprise Application Architecture, he defines Identity Map with the following two phrases:

1- Identity Map Ensures that each object gets loaded only once by keeping every loaded object in a map.

2- Looks up objects using the map when referring to them.


Article content
identity map

How Queries Work In EF (The life of a query)

The following description is a high-level overview of the process each query goes through.

  1. The LINQ query is processed by Entity Framework Core to build a representation that is ready to be processed by the database provider. The result is cached so that this processing does not need to be done every time the query is executed.
  2. The result is passed to the database provider. The database provider identifies which parts of the query can be evaluated in the database. These parts of the query are translated to database-specific query language (for example, SQL for a relational database). A query is sent to the database and the result set returned (results are values from the database, not entity instances)
  3. For each item in the result set: If the query is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance If so, the existing entity is returned If not, a new entity is created, change tracking is set up, and the new entity is returned. If the query is a no-tracking query, then a new entity is always created and returned


Is SingleOrDefault and FirstOrDefault returning cached data when loading the data for second time ?

yes !!!

//  1.  Load a [User] record from our database 
int chosenUserID = 851;
User usr = dbContext.Users.FirstOrDefault(s => s.Id == chosenUserID);

//  2. Call a web service, which updates that [User] record (or manually change the user properties)
HttpClient client = new HttpClient()
await client.PostAsync("http://someUrl", someContent);

//  3. Attempt to load an updated copy of the [User] record
User updatedUser = dbContext.Users.FirstOrDefault(s => s.Id== chosenUserID);        
It turns out that Entity Framework uses the Identity Map pattern. This means that once an entity with a given key is loaded in the context’s cache, it is never loaded again for as long as that context exists. So when we hit the database a second time to get the users, it retrieved the updated 851 record from the database, but because user 851 was already loaded in the context, it ignored the newer record from the database.

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(ChangeTracker). 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 (User Id).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(DbContext) object has its own object cache


but If you really really need to reload an entity that already is in cache, you could do something weird like this

private User GetUserById(int id) {
    //check if it's in the cache already
    var cachedEntity = dbContext.ChangeTracker.Entries<User>()
                           .FirstOrDefault(p => p.Entity.Id == id);
    if (cachedEntity == null) {
        //not in cache - get it from the database
        return dbContext.Users.Find(id);
    } else {
        //we already have it - reload it
        cachedEntity.Reload();
        return cachedEntity.Entity;
    }
}        

or use

User user = dbContext.Users.AsNoTracking().Find(id)        

Keep in mind that if you do this and make changes to that object, your changes won't be //saved when you call SaveChanges()


Tracking vs. No-Tracking Queries

Tracking behavior controls if Entity Framework Core keeps information about an entity instance in its change tracker. If an entity is tracked, any changes detected in the entity are persisted to the database during SaveChanges.

By default, queries that return entity types are tracking.

var user = dbContext.Users.SingleOrDefault(u => u.Id == 1);
user.Email= "test@test.com";
dbContext.SaveChanges();        

No-tracking queries are useful when the results are used in a read-only scenario. They're generally quicker to execute because there's no need to set up the change tracking information.

var users = dbContext.Users
    .AsNoTracking()
    .ToList();        

Difference of Find and First/Single

First(OrDefault)/Single(OrDefault):

First or Single methods always query the database and returns the object.

var userLinq1 = dbContext.Users.FirstOrDefault(u => u.Id == 2);  
Console.WriteLine(userLinq1.Name);  
var userLinq2 = dbContext.Users.FirstOrDefault(u => u.Id == 2);  
Console.WriteLine(userLinq2.Name);         
Article content
FirstOrDefault

Find():

If the key is already in memory and being tracked by the context, it avoids unnecessary database queries and returns the object which is already in tracking.

var userFind1 = context.Users.Find(2);  
Console.WriteLine(userFind1.Name);  
var userFind2 = context.Users.Find(2);   
Console.WriteLine(userFind2.Name);          
Article content
Find


To view or add a comment, sign in

More articles by Vanasis Baboomi

Others also viewed

Explore content categories