尝试避免嵌套的SqlConnection

3

我不确定嵌套的SqlConnection是否可行,但我真的希望远离它。现在我的代码中遇到了作用域问题,我正在尝试解决这个问题。

直到最近,我有一个全局的SqlConnection,当应用程序启动时打开并在结束时关闭。我现在发现了.NET连接池的概念,所以我改变了我的代码,使每个SqlCommand(或它们的组)使用自己的刚刚创建和打开的SqlConnection,信任.NET来管理池和相关的开销。

我目前遇到的问题是,我有几个代码块看起来像这样:

using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
{       
    sqlConnection.Open();
    using (SqlCommand sqlCommand1 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand1.ExecuteNonQuery();
    }
    using (SqlCommand sqlCommand2 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand2.ExecuteNonQuery();
    }
    .
    .
    .
    ClassGlobal.WriteAction(action);
}

ClassGlobal.WriteAction()函数大致如下:

public static void WriteAction(MyActionClass action)
{
    using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
    {       
        sqlConnection.Open();
        using (SqlCommand sqlCommand = new SqlCommand("Write the action to the DB", sqlConnection))
        {
            sqlCommand.ExecuteNonQuery();
        }
    }
}

如您所见,在第一个SqlConnection的作用域内调用了从WriteAction()创建的新SqlConnection。这并不好!我想避免这种情况发生。
过去,这不是问题,因为没有这些using (SqlConnection)块,而所有的SqlCommands都指向同一个(全局)SqlConnection。显然,我可以简单地将我的对WriteAction()的调用移到using (SqlCommand)的闭合括号下面,但:
  1. 我传递给它的action实例通常是在SqlConnection的作用域内实例化和填充的,所以我必须做更多的改变(很多),将其移出SqlConnection的作用域。这里有很多,会很困难。
  2. 实际上,我希望WriteAction()调用可以在SqlConnection的范围内,就像上面的示例一样,这样我就可以将其全部包装在TransactionScope中,这是以前不可能的,但肯定是个好主意。
所以,这是我计划要做的事情,但我想听听你们是否认为这不是好的做法,或者您是否可以建议更好的方法。(我最近发现我的全局SqlConnection不是一个好的实践,这导致了大量的时间修复它。我想避免在未来发现这样的问题)。如何将参数添加到WriteAction()函数中,以使其看起来如下:
public static void WriteAction(MyActionClass action, SqlConnection sqlConnection)
{   
    using (SqlCommand sqlCommand = new SqlCommand("Write the action to the DB", sqlConnection))
    {
        sqlCommand.ExecuteNonQuery();
    }
}

这意味着,我不必将对WriteAction()的调用移到SqlConnection的范围之外,而是可以将SqlConnection作为函数的参数添加,以便函数内部的SQLCommand使用相同的连接,即使该连接已被加入TransactionScope中。对于少数情况下从没有SqlConnection范围调用WriteAction()的情况,可以编写一个重载的函数,看起来像这样:
public static void WriteAction(MyActionClass action)
{
    using (TransactionScope transactionScope = new TransactionScope())
    {
        using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
        {
            sqlConnection.Open();
            WriteAction(action, sqlConnection);
        }
        transactionScope.Complete();
    }
}

这个想法看起来不错,还是会在另外两年后后悔这个决定吗?


这个问题与IT技术无关,请提供更具体的翻译内容。


1
看起来对我来说没问题。然而,为单个操作添加事务的重载似乎不正确。我猜那只是举例说明。 - Jodrell
当然可以。我的WriteAction()函数实际上不仅仅有一个SQL操作。谢谢。 - Dewald Swanepoel
1个回答

2
SqlConnection实例传递给该方法是完全可以的。但是,我不确定您上次使用的过载方法没有SqlConnection是否是一个好主意。它隐藏了您应该更好地使用其他新负载的事实。它使代码编译,这会阻止您立即修复应该修复的代码。
请注意,using块不是问题,而是打开的连接。只要不打开连接,连接池就不需要打开物理连接。
因此,在调用WriteAction之前关闭连接也是可行的选择:
using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
{       
    sqlConnection.Open();
    using (SqlCommand sqlCommand1 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand1.ExecuteNonQuery();
    }
    using (SqlCommand sqlCommand2 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand2.ExecuteNonQuery();
    }
    // ...
    sqlConnection.Close();

    ClassGlobal.WriteAction(action);

    // ... perhaps open it again here
}

来自MSDN:

每当用户在连接上调用Open时,池化程序会查找池中是否有可用的连接。如果有,则返回该连接给调用者,而不是打开一个新连接。当应用程序在连接上调用Close时,池化程序将其返回到活动连接的池集合中,而不是关闭它。一旦连接被返回到池中,它就可以在下一次Open调用时被重复使用。

因此,您可以看到WriteAction中嵌套的using并不是问题,只要您不保持外部连接处于打开状态即可。请不要将连接实例与物理连接混淆。


谢谢。这绝对是非常好的建议。事实上,这是我第一次理解SqlConnection.Close()的目的。我一直想知道,因为结束花括号也会完成同样的工作。不过,我通常在WriteAction()调用后有更多的SqlCommand。您更喜欢传递连接还是在WriteAction()调用后再次调用sqlCommand.Open()? - Dewald Swanepoel
1
@DewaldSwanepoel:这是一个品味问题,没有更多的背景信息。ClassGlobal.WriteAction听起来好像可以从任何地方调用。那么期望调用者有一个SqlConnection就不太好了。另一个问题是,如果这样的全局方法没有上下文/类,是否是最佳实践。这违反了OOP原则。另一个问题是,您是否应该重构外部使用方法,通过为每个步骤创建新方法或提供一个存储过程来减少操作。 - Tim Schmelter

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