【发布时间】:2017-10-31 18:28:27
【问题描述】:
我有一个 ASP.NET Web API,它使用 SqlConnection 连接到数据库。我有一个数据访问层类,它有一个包含连接的实例变量。我这样做有几个原因:
- 调用代码可以覆盖 DAL 类的构造函数中的连接字符串(例如用于测试代码)
- 在某些情况下,API 控制器需要打开 SQL 连接,开始事务,然后在提交(或可能回滚)事务之前调用 DAL 类中的多个方法。因此,关闭和重新打开每个方法的连接将不起作用,因为我必须保持连接打开(甚至将
SqlTransaction对象保持在范围内 - 我会通过将其设为实例变量来做到这一点)以便在 DAL 调用之间不回滚事务。 - 它还简化了代码的可读性,因此您无需到处复制代码来打开 SQL 连接。
当我对我的 API 进行压力测试时,通过每秒向它提供数百个请求,我遇到了 SQL 连接池耗尽问题。进一步调查表明,这似乎是因为 SQL 连接没有被释放。
我确实了解IDisposable 模式,但我不确定在这种情况下如何使用它。这是我的问题:
- 使用
using块或try/catch/finally块都需要在一个方法中创建、使用和完成对象。在上面的示例中,可能需要在多个方法调用中保持事务,这是不可能的。 - Microsoft(以及 SO 上的其他帖子)建议不要在对象析构函数中简单地调用
Dispose(),而是建议您执行我在问题 1 中指定的任一选项。 - Microsoft 还说您不应该自己实现
IDisposable来包装另一个管理对象的Dispose方法,而是应该“在完成后简单地调用该对象上的 Dispose()”。当控制器完成使用 SQL 连接时,当我不确定 DAL 中的情况时,我将如何在这种情况下执行此操作? (另外,这意味着我必须重构控制器,以便将对 DAL 的每个调用包装在using块中,所以它只是把罐子踢到了路上。)
理想的解决方案是能够以某种方式安排在控制器完成处理并将其响应返回给 SqlConnection 对象时调用服务器传送到前端。要“手动”执行此操作,我将不得不违反上面的第 3 点,并在我的 DAL 上创建我自己的 Dispose 方法,该方法又简单地调用 SqlConnection 的 Dispose。这也意味着我必须重构所有控制器中的许多方法,以将所有 DAL 访问包装在 using 块中。当控制器返回时,ASP.NET 似乎不会自动调用Dispose,这就是连接泄漏的原因。
在任何一种情况下,它都会产生更冗长的代码。例如:
// we only need one method call from the DAL, so let's be compact
string someData = new DAL().GetSomeData(someParam);
现在必须写成:
// we have to initialize here to keep the variable from falling out of scope after the using blocks
// we also must provide some value because the using block implies try/catch.
string someData = "";
using (DAL d = new DAL()) {
someData = d.getSomeData(someParam);
}
推荐的实现方式是什么?
在更通用的层面上,您实际上如何处理必须在方法调用之间持续存在的一次性对象(例如,作为实例变量)?在 try/catch/finally 或 using 等结构中使用一次性用品的需求似乎仅限于可以在单个方法中创建和处置对象的情况。
【问题讨论】:
-
如果你使用 DI 容器 - 你可以让它在请求结束时释放你的资源。虽然我个人更喜欢自己管理连接 - 在需要时打开、做事、关闭。
标签: c# dispose sqlconnection