2017-05-31 31 views
0

我有一个ASP.NET Web API,它使用SqlConnection连接到数据库。我有一个数据访问层类,它有一个包含连接的实例变量。我这样做了几个方面的原因:在ASP.NET中正确配置SqlConnection?

  1. 调用代码可以覆盖在DAL类(例如,用于测试代码)
  2. 也有一些情况下API控制器需要打开的构造函数的连接字符串一个SQL连接,开始一个事务,然后在提交(或回滚)事务之前调用DAL类中的几个方法。因此,关闭并重新打开每个方法的连接将不起作用,因为我必须保持连接处于打开状态(甚至保留SqlTransaction对象的范围 - 我会通过将它作为一个实例变量来实现),以便不必该事务在DAL调用之间回滚。
  3. 它还简化了代码的可读性,所以你不会复制遍布各处的代码来打开SQL连接。

当我对API进行压力测试时,通过每秒提供数百次请求,我碰到了SQL连接池耗尽问题。进一步调查显示,这似乎是因为SQL连接没有被处置。

我明白IDisposable模式,但我不确定在这种情况下如何使用它。这里是我的问题:

  1. 使用using块,或try/catch/finally块,都需要被创建,使用和单一方法中完成的对象。在可能需要跨多个方法调用持续存在的事务上面的示例中,这是不可能的。
  2. 微软(和SO其他职位)建议针对干脆把呼叫Dispose()在对象的析构,而不是建议你做任何的我在问题中指定的选项1.
  3. 微软还表示,你不应该实施IDisposable你自己只是为了包装另一个管理对象的方法Dispose,但是你应该“在完成对象时简单地调用对象的Dispose()。”在控制器完成使用SQL连接后,如果我不确定从DAL内部执行此操作,我该怎么做? (另外,这意味着我必须重构控制器来包装每次调用DAL在using块,所以它只是拖延时间的道路。)

对我来说,理想解决方案将是当控制器完成处理并且将其对服务器的响应返回以传送到前端时,能够以某种方式安排在SqlConnection对象上调用Dispose方法。要做到这一点“手工”,我将不得不违反上面的第3点,并在我的DAL上创建我自己的Dispose方法,然后简单地调用SqlConnection的Dispose。此外,这意味着我必须重构所有控制器中的许多方法以包装using块中的所有DAL访问权限。看起来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/finallyusing这样的结构中使用一次性物品的需要似乎限制了它们的使用,仅限于可以在单一方法中创建和处理物体的情况。

+1

如果您使用DI容器 - 您可以让它在请求结束时处理您的资源。虽然我个人更喜欢自己管理关系 - 当我需要时打开,做些东西,关闭。 – Evk

回答

0

由于MSDN recommends你应该使用

using(var cn = new SqlConnection(xx)){ cn.Open(); }

注意:不是每次

关闭SqlConnection吸引了来自连接池打开的连接无法打开新的连接,如果 一个是可用。否则,它建立到SQL Server实例的新连接。

而且为了控制好你的交易 - 你可以只使用TransactionScope()(虽然它可能需要MSDTC in some case`s


否则,如果使用TransactionScope是不是一种选择,并且要明确控制您的交易,唯一的选择是一起传递连接。因为它是discussed in this POST

你可以通过两种方式做“传递”。手动或使用依赖注入(DI)。所有DI容器都允许您控制实例的生命周期。所以你的情况可能是一个“每个请求一生” 样本可以this post

+0

这是大多数情况,但您需要确保连接字符串不会禁用连接池,但这不是默认行为,但可能会发生。 – Zalomon

+0

基于这些答案,似乎没有办法做到这一点,至少没有TransactionScope ...这太糟糕了,它似乎打败了将DAL分离成单独的类的整个观点。只要Web API必须管理创建和处置连接,您就失去了将DAL分开的好处(例如,只需编写新的DAL,甚至使用模拟的DAL进行测试即可替换DBMS)。看起来最干净的选择可能是简单地将DAL设置为一次性使用,然后在Web API中为DAL使用',即使这违反了“推荐”的做法。 – fdmillion

+0

您引用的文章描述了我的确切问题:“有时候有助于提升与您所从事工作的班级成员的联系,但这可能会造成尴尬局面 - 并且控制连接的生命周期和处置变得复杂对象(因为它通常排除使用using语句)“。所以唯一能做到这一点的方法是使DAL一次性使用,并在每个控制器方法中使用''using' ... – fdmillion

0

可以找到关于这一段。

在一个更通用的平面上,您如何实际处理必须在方法调用之间持续存在的一次性对象(例如作为实例变量)?像try/catch/finally或using这样的构造中使用一次性构件的需求似乎限制了它们的使用,只能用于可以在单一方法中创建和处理对象的情况。

作为一个经验法则负责实例一次性对象应是一个负责设置它的方法,它是确定沿着传递作为参数传递给其它方法从实例化的方法,但调用,这些方法不应该负责处理这些对象。这个规则的例外可能是如果你实例化对象以注入到另一个类中,但是在这种情况下,这个类应该实现IDisposable,并且应该在调用者使用它之后处理。

+0

这很有道理,它只是不遵循我正在使用的逻辑模型...正如我上面所说的,只要您需要Web API创建'SqlConnection'对象并将它们传递到DAL中,您就失去了分离DAL的任何好处(例如只需更换DAL即可完全替换DBMS )。这同样适用于其他场景。 DI当然有它的位置,但它有时也违反了“组件”的想法,在这种情况下,你有一个类可以提供你需要访问数据库的所有东西... – fdmillion

+0

我认为你违反了你的设计时该API有时会实例化SQLConnection而不是DAL;事实上,API不应该知道任何关于SQLConnection的存在,您应该有一个共同的入口点来创建和处理连接 – Zalomon