如何正确管理DataContext的处理?

时间:2011-03-21 18:10:48

标签: c# linq-to-sql datacontext dispose idisposable

我有一个非常重的数据库访问的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服务请求的持续时间。

我在互联网上找到了this article,它有一个DataContextFactory,似乎完全符合我的需要。

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

  

无法访问已处置的对象。

     

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

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

所以,我的问题是......在我浏览整个数据访问层并删除usings的负载之前,执行此操作的正确方法是什么?我不想通过取出usings来导致内存泄漏,但同时我想在不同的数据访问对象之间共享我的datacontext。

我应该删除usings,并在从Web服务请求返回之前手动调用dispose方法吗?如果是这样,那我该如何确保捕获所有内容,记住我有几个try-catch块可能会变得混乱。

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

更新

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

据我所知,由于以下两个原因之一与负载相关:

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

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

3 个答案:

答案 0 :(得分:3)

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

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

  2. 创建一个专门的IHttpModule,将事件附加到请求的末尾,您可以按照上述相同的步骤进行操作。

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

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

答案 1 :(得分:3)

我自己设法解决了这个问题......

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

public abstract class MyBase {

    protected static DataContext db = null;

    protected static DataContext GetDataContext() {
        return new DataContext("My Connection String");
    }

    // rest of class
}

然后,在继承了我想查询的MyBase的类中,我有这样的陈述:

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

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

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

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

protected static DataContext db = null;

到此:

protected DataContext db = null;

...这会破坏静态方法中的所有using语句。但是,可以通过在using中声明DataContext变量来轻松修复此问题,如下所示:

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

答案 2 :(得分:0)

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

IEnumerable<Father> fathers;
using (var db = GetDataContext())
{
  // Assume a Father as a field called Sons of type IEnumerable<Son>
  fathers = db.Fathers.ToList();
}

foreach (var father in fathers)
{
  // This will get the exception you got
  Console.WriteLine(father.Sons.FirstOrDefault());
}

可以通过强制它加载所有引用的对象来避免这种情况:

IEnumerable<Father> fathers;
using (var db = GetDataContext())
{
  var options = new System.Data.Linq.DataLoadOptions();
  options.LoadWith<Father>(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());
}