简体   繁体   English

在ASP.NET中正确地放置SqlConnection?

[英]Properly disposing a SqlConnection in ASP.NET?

I have an ASP.NET Web API that uses an SqlConnection to connect to a database. 我有一个使用SqlConnection来连接到数据库的ASP.NET Web API。 I have a data access layer class which has an instance variable containing the connection. 我有一个数据访问层类,该类具有包含连接的实例变量。 I do this for a couple of reasons: 我这样做有两个原因:

  1. the calling code can override the connection string in the constructor of the DAL class (eg for test code) 调用代码可以覆盖DAL类的构造函数中的连接字符串(例如,用于测试代码)
  2. there are some cases where the API controller needs to open an SQL connection, begin a transaction, then call several methods in the DAL class before committing (or maybe rolling back) the transaction. 在某些情况下,API控制器需要打开SQL连接,开始事务,然后在提交(或回滚)事务之前调用DAL类中的多个方法。 So, closing and re-opening the connection per method will not work because I have to hold the connection open (and even keep the SqlTransaction object in scope - I'd do that by making it also an instance variable) in order to not have the transaction rolled back between DAL calls. 因此,关闭和重新打开每个方法的连接将不起作用,因为我必须保持连接处于打开状态(甚至将SqlTransaction对象保持在作用域内-我会通过使其也成为实例变量来做到这一点),以便不具有事务在DAL调用之间回滚。
  3. It also simplifies code readability, so you're not duplicating code all over the place to open SQL connections. 它还简化了代码的可读性,因此您不必在各处复制代码来打开SQL连接。

When I stress-tested my API, by feeding it hundreds of requests per second, I hit up against the SQL connection pool exhaustion issue. 当我对API进行压力测试时,通过每秒提供数百个请求,我遇到了SQL连接池耗尽的问题。 Further investigating shows that this appears to be because the SQL connection is not being disposed of. 进一步的调查表明,这似乎是因为未处理SQL连接。

I do understand the IDisposable pattern, but I am not sure how I would use it in this scenario. 我确实了解IDisposable模式,但不确定在这种情况下如何使用它。 Here's my problem: 这是我的问题:

  1. Using the using block, or the try/catch/finally block, both require the object to be created, used and finalized within a single method. 使用using块或try/catch/finally块,都需要在单个方法中创建,使用和完成对象。 In the example above of a transaction that may need to persist across multiple method calls, this is not possible. 在上面的示例中,可能需要在多个方法调用之间保持持久的事务,这是不可能的。
  2. Microsoft (and other posts on SO) has recommended against simply putting a call to Dispose() in an object destructor, instead suggesting you do either of the options I specified in problem 1. Microsoft(以及SO上的其他文章)建议不要在对象析构函数中简单地调用Dispose() ,而建议您执行我在问题1中指定的任一选项。
  3. Microsoft also says you shouldn't implement IDisposable yourself just to wrap around another manage object's Dispose method, but instead you should "simply call Dispose() on the object when you're finished with it." 微软还说,您不应该自己实现IDisposable来包装另一个管理对象的Dispose方法,而应该“在完成该对象后简单地在该对象上调用Dispose()”。 How would I do this in this scenario when I am not sure from within the DAL when the controller is finished using the SQL connection? 当我不确定从DAL内部完成控制器使用SQL连接后,如何在这种情况下执行此操作? (Also, this would mean I have to refactor the controller to wrap each call to the DAL in a using block, so it's just kicking the can down the road.) (此外,这意味着我必须重构控制器,以将对DAL的每个调用包装在using块中,因此它只是将罐子踢倒了。)

The ideal solution for me would be to be able to somehow arrange to have the Dispose method called on the SqlConnection object when the controller has finished processing and is returning its response to the server for delivery to the frontend. 对我而言, 理想的解决方案是能够以某种方式安排在控制器完成处理并将响应返回给服务器以传递到前端时,在SqlConnection对象上调用Dispose方法。 To do this "by hand" I would have to violate point 3 above and create my own Dispose method on my DAL that simply in turn calls the SqlConnection's Dispose . 要“手动”执行此操作,我将不得不违反上面的第3点,并在DAL上创建自己的Dispose方法,该方法又反过来调用SqlConnection的Dispose Also it would mean I have to refactor many methods in all controllers to wrap all DAL access in using blocks. 这也意味着我必须在所有控制器中重构许多方法,以将所有DAL访问包装在using块中。 It appears that ASP.NET will not automatically call Dispose when the controller returns, which is why the connections are leaking. 似乎当控制器返回时ASP.NET不会自动调用Dispose ,这就是连接泄漏的原因。

In either case, it also makes for more verbose code. 无论哪种情况,它都需要更多详细的代码。 For example: 例如:

// we only need one method call from the DAL, so let's be compact
string someData = new DAL().GetSomeData(someParam);

now has to be written out as: 现在必须写成:

// 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);
}

What would be the recommended way to implement this? 推荐的实现方式是什么?

On a more generic plane, how do you actually deal with a disposable object that must persist between method calls (eg as an instance variable)? 在更通用的层面上,您实际上如何处理必须在方法调用之间保留的一次性对象(例如,作为实例变量)? The need to use disposables within constructs like try/catch/finally or using seems to limit their use only to situations where the object can be created and disposed within a single method. 在诸如try/catch/finally之类的构造中使用一次性用品或using似乎将它们的使用仅限于可以在单一方法中创建和处置对象的情况。

As MSDN recommends you should use the MSDN建议,您应该使用

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

NOTE: it does NOT open a new connection each time 注意:每次不会打开新连接

The SqlConnection draws an open connection from the connection pool if one is available. 如果有连接,则SqlConnection从连接池中绘制一个打开的连接。 Otherwise, it establishes a new connection to an instance of SQL Server. 否则,它将建立与SQL Server实例的新连接。

And in order to controll Your transactions - You can just use the TransactionScope() (although it may require MSDTC in some case`s ) 并且为了控制您的交易-您可以只使用TransactionScope() (尽管在某些情况下可能需要MSDTC


Otherwise, if using TransactionScope is NOT an option, and You want to explicitly control Your transactions, the only option is to pass the connection along. 否则,如果不是使用TransactionScope的选项,并且您想显式控制您的事务,则唯一的选择就是传递连接。 As it is discussed in this POST 正如本POST中讨论的那样

You can do the "passing along" in 2 ways. 您可以通过两种方式进行“传递”。 Manually or use Dependency Injection ( DI ). 手动或使用依赖注入( DI )。 All of the DI containers allow You to control the lifetime of the instance. 所有的DI容器都允许您控制实例的生存期。 So in Your case it might be a " per request lifetime " a sample can be found in this post 因此,在您的情况下,这可能是“ 每个请求生命周期 ”,可以在此帖子中找到一个示例

Regarding this paragraph. 关于本段。

On a more generic plane, how do you actually deal with a disposable object that must persist between method calls (eg as an instance variable)? 在更通用的层面上,您实际上如何处理必须在方法调用之间保留的一次性对象(例如,作为实例变量)? The need to use disposables within constructs like try/catch/finally or using seems to limit their use only to situations where the object can be created and disposed within a single method. 在诸如try / catch / finally之类的构造中使用一次性用品或使用的需求似乎将它们的使用仅限于可以在单一方法中创建和处置对象的情况。

As a rule of thumb the method responsible for instantiating a disposable object should be the one responsible of disposing it, it is ok to pass it along as an argument to other methods that are called from the instantiating methods but ,these methods, should not be responsible of disposing the objects. 根据经验,负责实例化一次性对象的方法应该是负责处理该对象的方法,可以将其作为参数传递给从实例化方法调用的其他方法,但是这些方法不应负责处理物体。 The exception to this rule might be if you instantiate the object to inject into another class, but in this case this class should implent IDisposable and should be disposed by the invoker after it is used. 如果实例化要注入到另一个类中的对象,则可能是此规则的例外,但是在这种情况下,该类应具有IDisposable属性,并且在使用后应由调用者处置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM