如何正确管理DataContext的处理?

我有一个非常重的数据库访问的Web服务。 它在测试中运行良好,但是一旦我将它投入生产并加速加载,它就会开始产生在DataContext中调用某个方法时引发的错误。 错误通常是以下之一:

你调用的对象是空的

无法访问已处置的对象。 对象名:’Dispose后访问的DataContext。’。

但不总是。

任何单个Web服务请求都可能导致多达10或15个数据库查询以及1或2个更新。

我设计了一个带有数据访问层的应用程序,这是一组对象,表示我的数据库中包含所有业务逻辑的表。 这是我的Web服务的一个单独项目,因为它与Web GUI共享。

数据访问对象派生自一个基类,该基类具有GetDataContext()方法,可在需要时启动数据上下文的实例。

我写的所有数据访问对象都是:

 using (db = GetDataContext()) { // do some stuff } 

为每个数据库交互愉快地创建/使用/处置我的DataContext(由sqlmetal.exe创建)对象。

经过几个小时的搔抓,我想我已经确定我的错误的原因是在加载时datacontext对象被创建和处理的方式太多了,我需要更改东西以共享相同的datacontext持续时间Web服务请求。

我发现这篇文章在互联网上有一个DataContextFactory,似乎完全符合我的需要。

但是,现在我已经实现了这个,并且DataContext被保存为HttpContext中的一个项目,我得到…

无法访问已处置的对象。

对象名:’Dispose后访问的DataContext。’

…每当我的datacontext被多次使用时。 这是因为我的use using (...) {}代码在首次使用后处理我的datacontext。

所以,我的问题是……在我浏览整个数据访问层并删除大量使用usings ,这样做的正确方法是什么? 我不想通过取出使用来导致内存泄漏,但同时我想在不同的数据访问对象之间共享我的datacontext。

我应该只是删除使用,并在我从Web服务请求返回之前手动调用dispose方法吗? 如果是这样,我怎么去确保我捕捉到所有的东西,记住我有几个可能变得凌乱的try-catch块。

还有另一种更好的方法吗? 我应该忘记处理并希望一切都被隐瞒了吗?

UPDATE

问题似乎不是性能问题……请求处理非常快,不超过200毫秒。 事实上,我已经通过生成大量虚假请求进行负载测试而没有任何问题。

据我所知,它与负载有关的原因有两个:

  • 大量请求会导致并发请求相互影响
  • 问题更频繁地发生,因为有很多请求。

当问题确实发生时,应用程序池将进入错误状态,并需要循环才能使其再次运行。

虽然我更喜欢使用using的工作单元方法,但有时它并不总是适合您的设计。 理想情况下,您需要确保在完成SqlConnection时释放它,以便其他请求有可能从池中获取该连接。 如果这是不可能的,那么您需要的是确保在每个请求之后处理上下文。 这可以通过以下两种方式完成:

  1. 如果您正在使用WebForms,则可以在页面生命周期结束时处理DataContext 。 检查HttpContext.Items集合以确定最后一页是否有数据上下文,如果有,则将其丢弃。

  2. 创建一个专用的IHttpModule ,它将事件附加到请求的末尾,您可以像上面那样执行操作。

上述两种解决方案的问题在于,如果您负载过重,您会发现很多请求都在等待连接可用,这可能会超时。 你必须权衡风险。

总而言之,工作单元方法仍然受到青睐,因为您在不再需要时立即释放资源。

我自己解决了这个问题……

我有一个基类,有一个方法可以创建DataContext实例,如下所示:

 public abstract class MyBase { protected static DataContext db = null; protected static DataContext GetDataContext() { return new DataContext("My Connection String"); } // rest of class } 

然后,在inheritanceMyBase的类中,我想要查询,我有这样的语句:

 using (db = GetDataContext()) { ... } 

问题是,我想从静态方法和非静态方法访问数据库,所以在我的基类中,我将db变量声明为静态…大错!

如果DataContext变量被声明为静态,那么在繁重的负载期间,当同时发生许多事情时,DataContext在请求之间共享,如果DataContext上的某些事情恰好同时发生,它会搞砸DataContext的实例,以及存储在应用程序池中的数据库连接,用于所有后续请求,直到它被回收,并刷新数据库连接。

所以,简单的解决方法是改变这个:

 protected static DataContext db = null; 

对此:

 protected DataContext db = null; 

…将破坏静态方法中的所有using语句。 但是这很容易通过在using声明DataContext变量来修复,如下所示:

 using (DataContext db = GetDataContext()) { ... } 

例如,如果您有一个引用另一个对象的对象(即两个表之间的连接),并且在处理完上下文后尝试访问引用的对象,则会发生这种情况。 像这样的东西:

 IEnumerable fathers; using (var db = GetDataContext()) { // Assume a Father as a field called Sons of type IEnumerable fathers = db.Fathers.ToList(); } foreach (var father in fathers) { // This will get the exception you got Console.WriteLine(father.Sons.FirstOrDefault()); } 

这可以通过强制它加载所有引用的对象来避免,如下所示:

 IEnumerable fathers; using (var db = GetDataContext()) { var options = new System.Data.Linq.DataLoadOptions(); options.LoadWith(f => f.Sons); db.LoadOptions = options; fathers = db.Fathers.ToList(); } foreach (var father in fathers) { // This will no longer throw Console.WriteLine(father.Sons.FirstOrDefault()); }