Ado.Net Entity Framework的使用


通過一段時間對Ado.Net Entity Framework的使用,感受到它的便利同時,也受到了一些困擾。其中最大的困擾,是源自AEF的設計理念,並不完全符合Web開發,以及有並發訪問要求的系統。

  最明顯的一點體現在緩存上。使用緩存是提高系統數據交互性能最簡捷有效的途徑,但如果想緩存Ado.Net Entity的話,並不是想像中那么簡單,比如:

?
var db = new DBEntities();
HttpContext.Current.Cache[ "product" ] = db.Products.ToList();

  如果你這樣寫了,那恭喜你,如果你試圖在另一個上下文環境中修改它,你會發現緩存成了禁區。系統會告訴你"一個實體對象不能由多個 IEntityChangeTracker 實例引用"。

?
var db = new DBEntites();
var product = = (Cache[ "product" ] as List<Product>).Single(p=>p.PId==id);
db.Attach(product); //試圖修改

  但是原來的那個IEntityChangeTracker哪去了呢,顯然它現在不可能再被移除了,除非你保留填充緩存時ObjectContext的引用。一開始,對這個問題,我通過新建一個Entity進行更新,然后把緩存對應項替換掉。這帶來了大量的令人不爽累贅代碼,因為不同類型實體類創建是不同的。如果業務邏輯稍復雜些,更新數據庫再對緩存同步的過程會更痛苦。而且隨着修改次數增多,內存中會充斥大量未釋放的ObjectContext對象。

  在AEF中,實體雖然有狀態,卻不能管理自己的狀態,必須依附於一個對象上下文中,對象上下文提供一個狀態管理器監視實體狀態。這和NHibernate等ORM框架大相徑庭,引起了國內外的論壇上許多抱怨。也許AEF團隊這樣設計有他們的考慮,可至少在Web開發中,確實導致了不便。

  於是我開始考慮如何干凈地移除那個麻煩的IEntityChangeTracker 實例引用,一種方式是用反射,這是個不得已的辦法。好在有更好的解決方案—在實體加入緩存前將它們Detach。在這里不得不再批評一下AEF API設計的不人性化,實在是想不明白,ObjectContext的Attach方法為什么不能直接判斷類型來添加實體,還需要指定實體集名稱呢?

  首先修改緩存過期的更新方法:

?
var db = new DBEntities();
var products = db.Products.ToList();
foreach (var p in products) db.Detach(p);
HttpContext.Current.Cache[ "product" ] = products;

  然后創建一個自己的ObjectContext,繼承自動生成的實體上下文類。

?
public class CustomDBEntites : DBEntities
{
     List< object > AttachedEntities = new List< object >();
 
     static List<Type> CachedTypes;  //系統緩存的實體類型
     static Dictionary<Type, string > EntitySets;     //實體類型對應的實體集名稱
 
     static CustomDBEntites ()
     {
         CachedTypes = new List<Type> { typeof (Section) };
 
         EntitySets = new Dictionary<Type, string >();
         EntitySets.Add( typeof (Product), "Products" );
         //.... ....
      }
     /// <summary>
     /// 使用指定的SaveOptions 將所有更新保存到數據源,並清除所有被更新實體的上下文引用。
     /// </summary>
     /// <param name="options">一個確定操作的行為的 System.Data.Objects.SaveOptions 值。</param>
     public override int SaveChanges(System.Data.Objects.SaveOptions options)
     {
         try
         {
             return base .SaveChanges(options);
         }
         finally
         {
             foreach (var item in AttachedEntities) this .Detach(item);
         }
     }
     /// <summary>
     /// 將對象附加到實體集上下文
     /// </summary>
     /// <param name="entity">對象實體</param>
     public void Attach(IEntityWithKey entity, EntityState state)
     {
         var type = entity.GetType();
         var cached = CachedTypes.Contains(type);
         this .AttachTo(EntitySets[type], entity);
         this .ObjectStateManager.ChangeObjectState(entity, state);
 
         //保存修改過的實體(除刪除)
         if (cached && state != EntityState.Deleted) this .AttachedEntities.Add(entity);
     }
     /// <summary>
     ///  將對象附加到實體集上下文,一般用於修改。
     /// </summary>
     /// <param name="entity">對象實體</param>
     public new void Attach(IEntityWithKey entity)
     {
         Attach(entity, EntityState.Unchanged);
     }
 
     /// <summary>
     ///  將對象附加到實體集上下文,並設置狀態為新加。
     /// </summary>
     /// <param name="entity">對象實體</param>
     public void AddObject( object entity)
     {
         Attach(entity as IEntityWithKey, EntityState.Added);
     }
 
     /// <summary>
     ///  將對象附加到實體集上下文,並設置狀態為刪除。
     /// </summary>
     /// <param name="entity">對象實體</param>
     public new void DeleteObject( object entity)
     {
         Attach(entity as IEntityWithKey, EntityState.Deleted);
     }
}

  這樣,我們就可以直接放心的進行Add/Attach或Delete了。Attach方法需要IEntityWithKey參數,DeleteObject和AddObject參數類型則是object,這應該也是AEF自己的混亂。在此,也無法覆蓋各個實體集諸如db.Products.AddObject這樣的方法,不過At Least, it works,and looks nice。作為應用開發程序員,這就足夠了。

  搞定了自己的程序,然后靜候AEF 5.0的到來。或許,也該試用下NHibernate等第三方框架了,我對AEF已經有點敬畏了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM