• 在Asp.Net中缓存Ado.Net Entity


      通过一段时间对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已经有点敬畏了。

  • 相关阅读:
    【SQL】CASE与DECODE
    【SQL】通过rowid查找及删除重复记录
    【SQL】联合语句
    【PLSQL】游标
    【SQL】IN、EXISTS和表连接三者的效率比较
    【SQL】CONNECT BY 层次化查询
    【SQL】MERGE
    【SQL】多表查询
    【Python算法】遍历(Traversal)、深度优先(DFS)、广度优先(BFS)
    【Python算法】归纳、递归、归简
  • 原文地址:https://www.cnblogs.com/XmNotes/p/1978495.html
Copyright © 2020-2023  润新知