Identity Map pattern in Entity Framework Core and Difference of Find And First/Single
In the design of DBMS the identity map pattern
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.
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.
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:
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
Recommended by LinkedIn
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 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);
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);