将一个打开的SqlConnection作为参数传递,还是在每个方法中调用一个新的连接更好?

20

如果我将调用的方法或函数需要打开一个已连接的 SqlConnection,我会在调用该函数的方法中打开它。例如:

protected static void btnSubmit(){
   conn.Open();
   myMethod(someParam, conn);
   conn.Close();
}

protected static void myMethod(object someParam, SqlConnection conn){
   //Some SQL commands etc here..
}

我这么做是为了:

  • 每个进程只打开和关闭一个SqlConnection

不过,像这样结构化我的代码会更好吗:

protected static void btnSubmit(){
   myMethod(someParam);
}

protected static void myMethod(object someParam){
   SqlConnection conn = New SqlConnection(".....");
   conn.Open();
   //Some SQL commands etc here..
   conn.Close();
}
我认为这种结构的优点是:
  • 我不必为每种方法传递额外的参数
  • 如果以后该方法不再具有SQL命令,则每次调用时不会使用未使用的参数
我认为这种结构的缺点是:
  • 如果myMethod是一个递归方法,那么当它调用自身时,它将打开另一个SqlConnection,以此类推。
  • 如果btnSubmit调用多个需要SqlConnection的方法,则每个方法都会打开和关闭一个新连接。
最佳做法是什么,哪种方法最常被使用?
2个回答

24

ADO.NET使用连接池,因此它会自动重用已经打开的连接,即使您认为自己正在打开一个新连接。在这种情况下,没有理由通过代码传递连接(作为参数)。这将使您的代码更加清晰,性能与将连接作为参数传递时相同。

更多细节请点击这里

另外(这真的很重要),请使用"using"关键字。这样,您就不必处理关闭连接和清理工作,因为现在编写的代码不处理关闭连接,所以在出现某些异常的情况下,可能会达到服务器上的连接限制。请使用类似以下的方式:

using(var connection = new SqlConnection(<connection_string>))
{
  connection.Open();
  using(var command = connection.CreateCommand())
  {

  }
}

正如您所看到的,您无需在finally块中调用connection.Close()或处理异常和关闭连接,因为这是“using”块的“工作”。

另外,一个重要的提示...事务不会通过连接池传递,因此如果您想跨方法调用保留事务,您必须传递您的连接(这是我能想到的唯一原因)。


所以,如果我打开100个不同的SqlConnection对象,都是为了同一个数据库/登录,这对处理过程没有任何额外的压力吗? - Curtis
感谢提供额外的细节。所以,把我的 using 语句放在 myMethod 中是个好习惯(使用我的例子)?我一直避免这样做,因为考虑到性能问题,但如果没有区别,这样做会更整洁! - Curtis
1
如果你不相信,可以进行测试并尝试一下 :) 你会发现没有区别(至少不明显)。连接池中有一个属性,指定最大池大小(我认为默认值是100),在这里看一下:http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.71).aspx,其中包含“使用连接字符串关键字控制连接池”部分。 - Aleksandar Vucetic
我添加了一个有关事务的注释,因为这是传递连接有意义的地方。 - Aleksandar Vucetic
如果我在“using”语句内部进行返回,无论是内部的还是外部的,那么这样做是否安全? - Hamzeh Soboh
显示剩余2条评论

13

使用最佳模式是Repository+UnitOfWork模式。

因此,创建Repository并传递包含连接的UnitOfWork。完成工作后,UnitOfWork被处理。

// Pseudocode
using(UnitOfWork uow = new UnitOfWork())
{
   Repository.Init(uow);
   Repository.SaveInDb(stuff);
}

还有工作单元:

// PseudoCode
class UnitOfWork : IDisposable
{
   public UnitOfWork()
   {
      conn = new SqlConnection();
      conn.Open();
   }

   public void Dispose()
   {
       conn.Close();
   }

 ....

}

这是我常用的方法。

有些人喜欢更简单的方法,即仓库拥有连接。这种方法更加简单,但如果你需要在多个仓库之间进行事务操作,则无法使用。


即使稍微提到在更新数据库时事务边界应该考虑的事实,也应该给予肯定。 - mwardm
如果您使用TransactionScope,即使每个存储库都有自己的连接,这些连接也会自动注册到已经生效的任何TransactionScope中,因此它可以在多个存储库之间完美地工作。有没有不这样做的原因? - Neutrino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接