代码改变世界

恰当的处理EF中的ObjectContext或是NHibernate session

2011-06-26 18:42  Zhang_Chen  阅读(1250)  评论(4编辑  收藏  举报

在使用各个ORM框架的时候,我们不免都会碰到这样的一些问题

  • 当我们希望使用延迟加载时ObjectContext已经被释放了
  • Attach一个实体时被告知不能被多个IEntityChangeTracker引用

image

例如下面的情况

namespace Demo.Business
{
    public class BPerson
    {
        public static void DoSomething(Person p)
        {
            p.Age++;//这里我们进行一些修改
            var db = new Entities();
            db.Person.Attach(p);
            db.ObjectStateManager.ChangeObjectState(p, EntityState.Modified);
            db.SaveChanges();
        }
.......
//PeopleController.cs
    [HttpPost]
    public ActionResult Edit(Person person)
    {
        if (ModelState.IsValid)
        {
            Business.BPerson.DoSomething(person);
            var db = new Entities();
            db.Person.Attach(person);//这里会抛出异常
            db.ObjectStateManager.ChangeObjectState(person, EntityState.Modified);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(person);
    }

显然解决的办法就是使用同一个ObjectContext的实例,
但是还有需要注意的一点:我们什么时候实例化它,怎么样得到它的引用又什么时候释放它?
我想到了
HttpContext.Items
我们可以方便的在程序的各个位置获得它的引用,于是上面的代码稍加修改:
     public static void DoSomething(Person p)
        {
            p.Age++;
            var db = GetDB();
            db.Person.Attach(p);
            db.ObjectStateManager.ChangeObjectState(p, EntityState.Modified);
            db.SaveChanges();
        }
        public static Entities GetDB()
        {
            var db = HttpContext.Current.Items["KEY_DB"] as Entities;
            if (db == null)
            {
                db = new Entities();
                HttpContext.Current.Items["KEY_DB"] = db;
            }
            return db;
        }
 
    [HttpPost]
    public ActionResult Edit(Person person)
    {
        if (ModelState.IsValid)
        {
            Business.BPerson.DoSomething(person);
            var db = Business.BPerson.GetDB();
            db.Person.Attach(person);
            db.ObjectStateManager.ChangeObjectState(person, EntityState.Modified);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(person);
    }

看起来不错。剩下释放的问题。
我们希望在整个http请求中使用同一个ObjectContext实例,所以就在请求结束的时候释放吧,像这样
        public static Entities GetDB()
        {
            var db = HttpContext.Current.Items["KEY_DB"] as Entities;
            if (db == null)
            {
                db = new Entities();
                HttpContext.Current.Items["KEY_DB"] = db;
                HttpContext.Current.ApplicationInstance.EndRequest += DisposeDB;
            }
            return db;
        }
        private static void DisposeDB(object sender,EventArgs e)
        {
            if (HttpContext.Current.Items.Contains("KEY_DB"))
            {
                var context = HttpContext.Current.Items["KEY_DB"] as Entities;
                if (context != null) 
                    context.Dispose();
            }
        }


或者你可以将实例化和释放都放到Global.asax.cs中。

希望对你有帮助