问题的提出

在前不久的一篇随笔中,我们讨论了是否需要手动执行DataContext的Dispose方法,最终的结论是不需要(即没有必要)。那么我们很自然会想到,既然不需要手动Dispose,那么是不是可以只使用一个DataContext而没有必要每次都new一个新的DataContext呢?比如使用传说中的单例模式。

实际上这样一来就乱套了。

首当其冲的是并发冲突问题。我们知道LINQ to SQL是对ADO.NET的封装,对于查询操作,使用的是DataReader。那么在多线程情况下如果两段代码先后使用同一个DataContext(单例)执行查询操作,必然会抛出DataReader未关闭的异常。

此外,由于DataContext会保存状态,还可能发生很多奇怪的问题。

既然使用全局唯一的DataContext是不可取的,那么我们可不可以在每一个数据访问类中使用一个DataContext呢?

public class SingleDataContextProductRepository : IProductRepository
{private NorthwindDataContext db;public SingleDataContextProductRepository(){db = new NorthwindDataContext();}public void InsertProduct(Product p){db.Products.InsertOnSubmit(p);db.SubmitChanges();}public void UpdateProduct(Product newProduct){db.SubmitChanges();}public Product GetProduct(int id){return db.Products.SingleOrDefault(p => p.ProductID == id);}
}

DataContext是轻量级的吗?

浪子同学前不久指出我滥用DataContext,他认为“datacontext是个很大的对象,应该避免不停地实例化”、“一个DataContext至少占有一个dbconnection,所以最好不要到处实例化你的DataContext,性能会有大影响”。

首先,我并不认为“由于DataContext维护一个DbConnection而不能到处实例化”是一个站得住脚的论据。因为DataContext在执行完查询和提交更改的操作之后,都会关闭Connection,一次实例化所带来的开销,与Connection无关。

那么DataContext究竟是不是一个“很大的对象”呢?空口无凭,还是请出源代码来说明问题。

在DataContext的构造函数中,核心的初始化代码都委托给了这样一个私有方法:

private void Init(object connection, MappingSource mapping)
{MetaModel model = mapping.GetModel(base.GetType());this.services = new CommonDataServices(this, model);this.conflicts = new ChangeConflictCollection();if (model.ProviderType == null){throw System.Data.Linq.Error.ProviderTypeNull();}Type providerType = model.ProviderType;if (!typeof(IProvider).IsAssignableFrom(providerType)){throw System.Data.Linq.Error.ProviderDoesNotImplementRequiredInterface(providerType, typeof(IProvider));}this.provider = (IProvider) Activator.CreateInstance(providerType);this.provider.Initialize(this.services, connection);this.tables = new Dictionary<MetaTable, ITable>();this.InitTables(this);
}

其中,MappingSource用来设置DataContext的映射方式:通过Attribute(AttrubiteMappingSource)还是通过XML配置文件(XmlMappingSource)。LINQ to SQL分别为这两种映射方式提供了MetaModel、MetaTable、MetaType、MetaParameter、MetaAssociation、MetaFunction等等。

DataContext根据MappingSource获取相应的MetaModel,在此基础上创建Provider和Tables,并进行初始化。在provider.Initialize中,SqlProvider对象对connection进行初始化,但并不Open,在执行查询和更新时才会Open。

可见,DataContext并没有占用多么庞大的资源,无非就是一个DbConnection和一些映射对象而已。

有人说它“大”,我想可能是看到它拥有所有表的映射吧。但那只是映射而已,没有任何数据。

MSDN中的描述是这样的:

A DataContext is lightweight and is not expensive to create. A typical LINQ to SQL application creates DataContext instances at method scope or as a member of short-lived classes that represent a logical set of related database operations.

可见DataContext是轻量级的,创建它不需要很大的开销。

直观上说,由于执行的初始化操作和占用的内存都要少,使用一个DataContext,肯定要比使用多个要“快”。但是在《LINQ in Action》中,却有这样一段话:

If we need to be able to update the data, be aware of the context’s scope and
manage it appropriately. In Windows applications, it may be acceptable to retain a context as changes are made, but realize the performance and memory overhead that come with retaining all objects and the changed values. The intended usage pattern for LINQ to SQL is as a unit of work, which uses the following pattern: Query – Report – Edit – Submit – Dispose. As soon as we no longer need to maintain changes on an object, we should clean up the context and create a new one.

也就是说变化跟踪服务会带来性能和内存的损耗,使用DataContext的推荐模式是将其作为工作单元(unit of work,即查询--显示--修改--提交--销毁。需要的时候就创建,不需要的时候就销毁。

我已经说服自己随时创建DataContext,倒是变化跟踪服务究竟会有多大的影响呢?还是来进行一下性能测试吧。

性能比较 I

与之前的测试一样,我们仍然使用Northwind数据库,并建立IProductRepository接口,然后创建SingleDataContextProductRepository和MultiDataContextProductRepository,并且捎带着测试一下释放DataContext的性能,创建一个DisposedDataContextProductRepository。在MultiDataContextProductRepository中,我们使用前面测试过的最快的委托方法。

所有的代码参见文后的下载链接。测试结果见下面的图。

循环1000次:

循环10000次:

可见单一DataContext的性能还是比多个DataContext高出不少,但恐怕远远不到“滥用”带来的毁灭性的影响。而是否Dispose反而对性能的影响并不十分明显。

性能比较 II

以上测试是在没有关联对象的情况下进行的,那么存在关联关系的情况下结果如何呢?而且以上查询的是单一的结果,如果取回一个集合,情况又会发生什么变化呢?我们假设要得到一个IEnumerable<Product>,并遍历这个集合,访问每一个Product的Category属性。因此在IProductRepository接口中添加一个GetProducts方法。

IEnumerable<Product> GetProducts(Expression<Func<Product, bool>> predicate);

由于DataContext在Dispose之后无法进行延迟加载,我们只能在GetProducts中通过DataLoadOptions对象将关联关系一次性加载进来,并在返回结果时调用ToList方法。

public IEnumerable<Product> GetProducts(Expression<Func<Product, bool>> predicate)
{using (NorthwindDataContext db = new NorthwindDataContext()){DataLoadOptions loads = new DataLoadOptions();loads.LoadWith<Product>(p => p.Order_Details);loads.LoadWith<Product>(p => p.Category);loads.LoadWith<Product>(p => p.Supplier);db.LoadOptions = loads;return db.Products.Where(predicate).ToList();}
}

结果会如何呢?循环1000次:

Dispose方法由于加载了大量关联对象,导致性能大幅下降。有没有避免使用DataLoadOption加载关联对象的方法呢?我暂时没有想到,有知道的朋友不妨提示一下。

如何取舍

单从这个测试来看,一个DataContext明显要优于多个DataContext。而且我没有看出变化跟踪服务对性能的影响。也许这个例子并不合适,也请大家指出。在实际开发中,情况是否会有变化,也有待验证。

我想要说明的还是立即加载的问题。由于一个DataContext只能设定一次DataLoadOptions,那么如果需要立即加载关联对象时,就只能在SingleDataContext的构造函数中设置LoadOptions,这样整个性能将不堪设想。

MSDN中对于DataContext的说明是:用的时候创建,不用的时候销毁。在多层架构中,“不用的时候销毁”这条已经被证明是低效的了,但本文的测试还无法完全否定“用的时候创建”这条建议。

如果你有什么好的想法,请一定要告诉我。

代码下载

转载于:https://www.cnblogs.com/kirinboy/archive/2010/01/29/use-one-datacontex-or-not.html

使用一个DataContext,还是多个?相关推荐

  1. 如何使用Action.Invoke()触发一个Storyboard

    如何使用Action.Invoke()触发一个Storyboard 原文:如何使用Action.Invoke()触发一个Storyboard 一般在我们的项目中,最好是将Storyboard放在前台, ...

  2. Windows Phone开发(48):不可或缺的本地数据库

    原文:Windows Phone开发(48):不可或缺的本地数据库 也许WP7的时候,是想着让云服务露两手,故似乎并不支持本地数据库,所有数据都上传上"云"数据库中.不过呢,在SD ...

  3. 用日志记录LINQ中的所有增删改的SQL语句的方法

    我们知道LINQ中的增删改都要调用SubmitChanges方法,我们记录所有SQL的方式就是重写(override)DataContext中的SubmitChanges方法,为了避免每次修改dbml ...

  4. LINQ To SQL 语法及实例大全

    LINQ to SQL语句(1)之Where Where操作 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子 ...

  5. Linq to sql与EF零碎知识点总结

    ------------------------------第一天(2013-3-25) 1.ado.net实体模型,(Ef) 2.创建上下文对象: 调用相应方法,最后调用.savechanges() ...

  6. 在.NET 3.5 平台上使用LINQ to SQL创建三层/多层Web应用系统(Part2) 转

    LINQ to SQL LINQ to SQL 提供了访问数据库的方法,且允许我们做所有数据库相关的操作,如查询.插入.更新和删除.LINQ to SQL消除了写存储过程和从数据访问层调用的过程,它可 ...

  7. Windows Phone DataBound ListBox中针对UIElement的事件绑定(Button)

    在很多使用DataBound的ListBox案例中,我们都监听了它的 SelectionChanged 事件,当我们用手指点击某一项时,可以从 ListBox.SelectedItem 属性上很容易获 ...

  8. DevExpress WPF MVVM入门例子

    设置界面(MainWindows.XAML) <Windowxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentati ...

  9. LINQ那些事儿(2)- 简单对象的CRUD操作和Association的级联操作

    从(1)我们看到,当生成entity class定义时,entity class或xml mapping文件中都已经完整的包含了entity和关系数据库的映射信息了,LINQ2SQL会根据这些信息来把 ...

  10. Linq to SQL 下篇

    上一篇博文主要是简单的介绍了使用自定义的一个对象映射模型来完成 Linq to SQL, 而这一篇博文则主要介绍使用由 VS 提供的设计器来实现 Linq to SQL , 说穿了,两个在实质上是根本 ...

最新文章

  1. 程序员的日常,过于真实 | 每日趣闻
  2. css3-巧用选择器 “:target”
  3. windows环境中利用NMake工具编译连接C++源代码
  4. HDU 2612 Find a way(BFS)
  5. python在mac上运行不用装模块_MAC OSX使用Python安装mysql模块问题
  6. 如何从ERP下载Sales BOM到CRM
  7. 【设计模式】设计模式C++编程实现之单例模式(Singleton Pattern)
  8. 2019-06-15
  9. 设备树与驱动的关系_9 Linux设备树的原理与应用实例(一)—— 什么是设备树...
  10. WMS软件哪个好?排名怎样
  11. 阿里巴巴矢量图标 iconfont 下载图标分辨率小一点、并占得内存小一点呢
  12. [渝粤教育] 西北农林科技大学 土壤学 参考 资料
  13. 华为服务器如何修改ip地址,华为路由器IP地址设置问题-192.168.1.1进不去
  14. 上“低代码”半年,30名程序员被裁,CTO离职!
  15. python 切片详解
  16. Verilog纠错记录
  17. 浅谈学术论文rebuttal
  18. Crytek的幕后花絮
  19. 谈谈IT行业的收入和一些生存之道
  20. 郑林楷计算机系姚班,郑林楷为什么被清华录取 郑林楷在清华很厉害吗

热门文章

  1. 怎样修复电动车电瓶。
  2. my97中文乱码问题
  3. Eclipse 导入项目与 svn 插件关联全过程记录
  4. HashMap的工作原理深入再深入
  5. 什么是产品的愿景—从一篇博文中学得到
  6. 数据转换软件公司——HYFsoft
  7. Bitmap对象保存到bmp文件中
  8. Mysql中给数据库,表,字段设置字符编码
  9. Unstated浅析
  10. Openstack api 学习文档 restclient使用文档