阿不

潜水

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  212 随笔 :: 0 文章 :: 3066 评论 :: 75 引用

公告

在LINQ2SQL和Entity Framework中都有类似的DataContext对象,它是整个数据映射的载体和数据操作的入口。DataContext是一个标准的Unity of Work的实现,它可以保证在一个DataContext上下文的多个数据操作,保持事务的原子性。DataContext还具有数据容器的性质,维护了所有操作数据的状态,它会跟踪您对所有检索到的实体所做的更改,并且保留一个“标识缓存”,该缓存确保使用同一对象实例表示多次检索到的实体。即使是LINQ2SQL和Entity Framework还有很多的不同,但是DataContext的行为都基本差不多。

DataContext承担这么重要的工作,很多人担心它是一个很重的对象,创建和销毁都需要消耗比较多的资源(比如映射关系的初始化等),因此寻思着是否可以对DataContext对象进行静态化,让整个程序只使用一个DataContext对象。希望通过这样,来尽量减少频繁创建和销毁DataContext所带来的性能损耗。可是事实确实如此吗?来看看下面的几点简单的分析吧:

  1. 在MSDN文档中,对LINQ2SQL DataContext的描述中,有特别说明了一点:“DataContext 是轻量的,创建它不需要很大的开销。典型的 LINQ to SQL 应用程序在方法范围内创建 DataContext 实例,或将这些实例创建为生存期较短的类(这些类表示相关数据库操作的逻辑集合)的成员。”也许是担心很多人对DataContext的误会,在MSDN中还特别说明了应该是在方法范围内,或者是作为生存周期转短的类的成员,以求尽量早的释放和回收DataContext资源。
  2. DataContext不是线程安全的对象,在MSDN中是这样描述的:“此类型的任何公共 static(在 Visual Basic 中为 Shared) 成员都是线程安全的。但不保证所有实例成员都是线程安全的。”没有线程安全的保证,会让我们在多线程共享DataContext时,出现很多随机的、意想不到的结果。比如,由于DataContext会缓存所有的数据操作,此时我们一个线程的数据修改到一半,之后还有其它的数据需要被修改,但是它却有可能被另一个线程在调用SaveChanges时被意外提交了。此类问题,经常都是随机的,而且一般是在开发时不容易被发现的,只有当程序在高并发的环境下才会频繁发生。
  3. DataContext还具有数据容器的性质,所有从DataContext取出来的数据都被会它所跟踪。它需要跟踪这些实体对象的变化情况,以便在SaveChanges的时候,决定对实体对象施加相应的操作,是添加,修改(还包括修改的字段),还是删除。同时,它一般还具有“标识缓存”的功能,它可以减少与数据库的交互次数,保证用户在同一个DataContext里面多次检索到同一行数据时以同一对象实例的表示。可以预见,如果DataContext静态化的情况下,那么对象容器里缓存的这些实体对象的释放和回收时机得不到保证,甚至在整个程序周期内无法得到释放,并且对象也无法得到及时的更新。

也许还有其它的可能,我并没有考虑到。但是基于以上3点,我们就可以得到答案。DataContext是不应该被静态化,或者是单例化的。然而很多时候,瞬时的DataContext也不能够满足需求,比如当我们需要从一个方法里面返回出一个实体对象,而这个实体对象是用方法内部自定例化的DataContext对象取出的。一旦在方法外部,想要持久化实体对象的修改时,由于无法得到DataContext实例而无法保存。此时,我们可能要频繁的把DataContext作为方法参数进行不断的传递,这样也太麻烦了。因此,在实践中,我们一般都会让DataContext在当前请求上下文保持单例,让DataContext一个请求上下文周期内单例既可以避免传递,还可以保证线程安全。

也有同事提出,让读用一个DataContext,写用另一个DataContext,用读写分离的概念来使用DataContext。虽然没有实践过,但还是得注意线程安全与对象的回收时机问题。总之,使用DataContext的静态化, 除非非常必要,否则在Web环境中这样使用,肯定会带来很多不必要的麻烦。

posted on 2010-10-10 13:03 阿不 阅读(...) 评论(...) 编辑 收藏