過去我們常常使用Where或First(FirstOrDefault)方法來查找對應的實體,比如:
- var query = context.CertInfoMakeDetails.ToList().Where(make => int.Parse(make.CertCode) >= startcode &&
- int.Parse(make.CertCode) <=
- (startcode + count - 1) &&
- make.ProductName == productName);
var query = context.CertInfoMakeDetails.ToList().Where(make => int.Parse(make.CertCode) >= startcode && int.Parse(make.CertCode) <= (startcode + count - 1) && make.ProductName == productName);
這樣做的缺點是:即使我們相應的實體已經被DbContext緩存,EF還是會去執行數據庫訪問,而數據庫訪問是被普遍認為比較耗費性能的。
從EF4.1開始,微軟為我們提供了一個新的API:DbSet<>.Find(),它可以幫助我們通過主鍵來查找對應的實體。並且如果相應的實體已經被DbContext緩存,EF會在緩存中直接返回對應的實體,而不會執行數據庫訪問。
比如我們查找證書ID為"101"的實體:
- var query = context.CertInfoMakeDetails.Find("101");
var query = context.CertInfoMakeDetails.Find("101");
也可以使用聯合主鍵(比如查找CertCode = "101", ProductName = "ABC")的實體:
- var query = context.CertInfoMakeDetails.Find("101", "ABC");
var query = context.CertInfoMakeDetails.Find("101", "ABC");
注意:此處輸入聯合主鍵的次序需要按照我們定義改實體類時聲明主鍵的次序。
和之前使用Where或First調用不同,Find也可以找到剛剛新增的實體:
- using (var context = new MyContext())
- {
- context.CertInfoMakeDetails.Add(new CertInfoMakeDetail {....});
- var newOne = context.Find(“Id”);
- }
using (var context = new MyContext()) { context.CertInfoMakeDetails.Add(new CertInfoMakeDetail {....}); var newOne = context.Find(“Id”); }
最后讓我們來看看Find是如何實現的:
- public TEntity Find(params object[] keyValues)
- {
- this.InternalContext.ObjectContext.AsyncMonitor.EnsureNotEntered();
- this.InternalContext.DetectChanges(false);
- WrappedEntityKey key = new WrappedEntityKey(this.EntitySet, this.EntitySetName, keyValues, "keyValues");
- object obj2 = this.FindInStateManager(key) ?? this.FindInStore(key, "keyValues");
- if ((obj2 != null) && !(obj2 is TEntity))
- {
- throw System.Data.Entity.Resources.Error.DbSet_WrongEntityTypeFound(obj2.GetType().Name, typeof(TEntity).Name);
- }
- return (TEntity) obj2;
- }
public TEntity Find(params object[] keyValues) { this.InternalContext.ObjectContext.AsyncMonitor.EnsureNotEntered(); this.InternalContext.DetectChanges(false); WrappedEntityKey key = new WrappedEntityKey(this.EntitySet, this.EntitySetName, keyValues, "keyValues"); object obj2 = this.FindInStateManager(key) ?? this.FindInStore(key, "keyValues"); if ((obj2 != null) && !(obj2 is TEntity)) { throw System.Data.Entity.Resources.Error.DbSet_WrongEntityTypeFound(obj2.GetType().Name, typeof(TEntity).Name); } return (TEntity) obj2; }
首先,EF調用了EnsureNotEntered() 方法來檢查內部ObjectContext的狀態,如果這個實例當前獲得了一個排它鎖,那么就拋出異常。之后調用DetectChanges 方法來同步了對象狀態。WrappedEntityKey 主要用來將Find函數的參數包裝成一個KeyValuePair。
之后,Find 方法首先調用 FindInStateManager 方法在緩存中進行查找,如果找不到對應的實體,那么就調用 FindInStore 在對數據庫進行查找,如果仍然找不到,就拋出異常,否則返回對應實體。