我有一个非常重的数据库访问的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毫秒。事实上,我已经通过生成大量虚假请求进行负载测试而没有任何问题。
据我所知,由于以下两个原因之一与负载相关:
当问题确实发生时,应用程序池将进入错误状态,并需要循环才能使其再次运行。
答案 0 :(得分:3)
虽然我更喜欢使用using
的工作单元方法,但有时它并不总是适合您的设计。理想情况下,您需要确保在完成任务后释放SqlConnection
,以便其他请求有可能从池中获取该连接。如果这是不可能的,那么您需要的是确保在每个请求之后处理上下文。这可以通过以下两种方式完成:
如果您正在使用WebForms,则可以在页面生命周期结束时处理DataContext
。检查HttpContext.Items
集合以确定最后一页是否有数据上下文,如果有,请将其丢弃。
创建一个专门的IHttpModule
,将事件附加到请求的末尾,您可以按照上述相同的步骤进行操作。
上述两种解决方案的问题在于,如果您负载过重,您会发现很多请求都在等待连接可用,可能会超时。你必须权衡风险。
总而言之,工作单元方法仍然受到青睐,因为您在不再需要时立即释放资源。
答案 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());
}