在DAL方法中重用SqlConnection

时间:2010-07-15 03:59:37

标签: c# sql-server-2008 .net-3.5 data-access-layer

假设我们有一个DAL方法

public void BuyProduct(int productId, int quantity, int buyerId);

在该方法中,我们需要调用2个存储过程:

  1. EXEC tblOrders_CreateNewOrder
  2. EXEC tblProducts_RecalculateStock
  3. 创建2个SqlCommands是一个好习惯 - 每个存储过程一个并使用单个SqlConnection来执行这些命令吗?

    OR

    为每个SqlCommand创建单独的SqlConnection是否更好?

    所以基本上我要问:在单个DAL方法中重复使用单个SqlConnection用于多个(2-4)SqlCommands是一个好习惯(显然在整个DAL中重用SqlConnection会很愚蠢)?

    PS - 请不要问我为什么不能将2个存储过程合并为1.我的答案是 - 分离关注点。

4 个答案:

答案 0 :(得分:5)

真正的问题不是连接,而是交易。当逻辑操作涉及多个DAL物理操作时,通常它们必须是转换的一部分。除非corectness是可选的...如果一个事务跨越多个连接,那么它必须被提升到一个分布式事务,带来灾难性的perofrmance结果。因此,在设计DAL时,始终能够将事务关联到连接。这通过DAL API设计涟漪,因为通常结果是必须将连接和事务对象明确地分发给DAL方法,作为单独的参数或作为聚合它们的“上下文”对象。

答案 1 :(得分:2)

是的,将一个SqlConnection重用于多个SqlCommands(在一个方法中)是一个好习惯。据我所知,在你的情况下你还需要像这样使用SqlTransaction:

public void SomeDALMethod(string connectionString)
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();

        var transaction = connection.BeginTransaction();

        var command1 = new SqlCommand("tblOrders_CreateNewOrder", connection, transaction)
                        {
                            CommandType = CommandType.StoredProcedure
                        };
        var command2 = new SqlCommand("tblProducts_RecalculateStock", connection, transaction)
                        {
                            CommandType = CommandType.StoredProcedure
                        };

        try
        {
            command1.ExecuteNonQuery();
            command2.ExecuteNonQuery();
            transaction.Commit();
        }
        catch (Exception ex)
        {
            // Commit failed
            try
            {
                transaction.Rollback();
            }
            catch (Exception ex2)
            {
                // Rollback failed
            }
        }           
    }
}

它允许您在第二次sp失败的情况下回滚第一个存储过程的执行。

答案 2 :(得分:1)

默认情况下,.NET会创建一个池化的SqlConnection。因此,创建新连接(前提是所有连接都使用相同的连接字符串)在池完全耗尽之前不会产生开销。

答案 3 :(得分:0)

此方法保持连接,只需在每次迭代时更改参数。只需提供正常的信息和一个返回参数列表的委托,基本上只是告诉它如何构建参数列表。类型T是您要从中构建参数列表的对象类型。仅当所有项目都失败时,它才返回false。

public static bool ExecuteBulkNonQuery<T>(string connectionString, CommandType commandType, 
            string commandText, IEnumerable<T> listItems, Func<T,SqlParameter[]> setParameters)
        {
            var fails = 0;
            using (var conn = new SqlConnection(connectionString))
            {
                using (var comm = new SqlCommand(commandText, conn))
                {
                    comm.CommandType = commandType;
                    conn.Open();
                    foreach (var obj in listItems)
                    {
                        comm.Parameters.Clear();
                        comm.Parameters.AddRange(setParameters.Invoke(obj));
                        fails += comm.ExecuteNonQuery();
                    }
                    return fails != 0;
                }
            }
        }