EF中DataContext以及对应实体的生命周期

Enitity Framework的文章非常多,而且使用起来也非常简单。当然一旦遇到一个异常时,就非常让人头疼。最近一直用EF,遇到一些问题,所以分享出来对大家或许有些帮助。在这里不会研究源码,只说原理,并分享一些最佳实践的代码。有说的不对的地方,请过路人指正。

DataContext对象应该生成多少个?

当我们new一个新的DataContext对象并从数据库获取相关实体时,意味着DataContext会对这些获取到的实体进行状态跟踪。本质上讲,你只得到数据库某个数据的一个拷贝。当你对实体进行增加删除时,实体本身并没有进行增删改,但DataContext会把这些状态都记录下来;一旦调用dbContext.SaveChanges(),DataContext才会生成对应sql命令,把这些更改保存到数据库中。如果你以为这是理所当然的事情,那么请看下面一个例子:

User user = null;
using(MyDataContext context = new MyDataContext())
{
   user = context.Users.First();
}
user.Age = 18;

using(MyDataContext context2 = new MyDataContext())
{
   context2.Entry<User>(user).State = EntityState.Modified;
   context2.SaveChanges();
}

 这里会抛出一个异常。异常是:An entity object cannot be referenced by multiple instances of IEntityChangeTracker。IEntityChangeTracker相当于实体的跟踪器。一个DataContext管理了另一个DataContext下的实体,这是不允许的。打个比方说,DataContext就像一个国王,负责他的子民的生老病死,另一个国家的国王是不能对本国的子民进行审判的,这是越界了,要引发战争的。【可以通过Attach和DeAttach来修改国籍,达到被另一个DataContext管理的目的】这么看来,在一个应用里似乎只需要一个国王就能搞定所有事情,事实是不是这样的呢?

上面说过,每个通过同一个DataContext得到的实体都会被它跟踪管理,那么如果多个客户端都使用一个DataContext获取实体,而又都在各自的客户端更改,当同时提交SaveChanges时,哦,麻烦出现了。永远不要出现单例的DataContext用在应用程序环境里,那会导致各种错误,最基本的是线程不安全,你对国王的各种奏章导致他分身乏术,出现并发冲突。

那么在一个应用里,要有多少个国王DataContext才行呢?

根据各种经验显示,对Asp.Net网站,应该是每一个请求一个DataContext,对于Winform以及WPF应该是每个窗体或表现一个DataContext,对于WebService应该是每个调用一个DataContext;


对于Asp.Net网站,因为是一个请求一个DataContext,所以我们可以保存到HttpRequst的缓存中,保证了DataContext的唯一性。最佳实践代码如下:

   public static MyDataContext Instance
    {
        get
        {
            if (HttpContext.Current != null && HttpContext.Current.Cache["MyDataContext "] == null)
            {
                HttpContext.Current.Cache["MyDataContext "] = new MyDataContext ();
            }
            return HttpContext.Current.Cache["MyDataContext "] as MyDataContext ;
        }
        set { 
            if(HttpContext.Current != null)
                HttpContext.Current.Cache["MyDataContext "] = value; 
        }
    }

 对于winform或WPF,可以使用如下代码:

      [ThreadStatic]
	private static MyDataContext instance;
 
	public static MyDataContext Instance(){
		if (context == null)
			instance= new MyDataContext ();
		return instance;
	}

使用ThreadStatic保证线程安全,为什么在asp.net下不使用这个属性来确保安全呢?是因为IIS下有重用线程的可能性,对于另一个请求,IIS会继续使用这个DataContext进行操作,那么,你看,又会并发冲突了。

posted @ 2012-11-16 17:42  Zigzag  阅读(2433)  评论(2编辑  收藏  举报