21

想知道是否建议将数据库连接对象传递给(到其他模块)或让方法(在其他模块中)负责设置它。我倾向于让方法设置它,以便在使用它之前不必检查连接的状态,并且只需让调用者将任何需要的数据传递给设置连接所需的调用方法。

4

9 回答 9

13

就我个人而言,我喜欢使用紧密范围的连接;晚点打开它们,使用它们,然后关闭它们(在“使用”块中,都在本地方法中)。在大多数情况下,连接池将处理重用连接,因此这种方法没有真正的开销。

过去,传递连接的主要优点是您可以传递交易;但是,TransactionScope这是一种在方法之间共享事务的更简单的方法。

由于这些类是特定于实现的,我会编写每个类来打开它自己的本机事务。否则,您可以使用 ado.net 工厂方法从配置文件(提供程序名称)创建适当的类型。

于 2008-10-30T20:33:16.013 回答
10

就个人而言,我喜欢使用 SetData 和 GetData在Thread Local Storage顶部存储我当前打开的连接和事务的堆栈。我定义了一个类来管理我与数据库的连接并允许它使用 dispose 模式。这节省了我传递连接和事务的需要,我认为这会使代码混乱和复杂化。

我强烈建议不要将其留给每次需要数据时打开连接的方法。这将导致一个非常糟糕的情况,即很难在整个应用程序中管理事务并且打开和关闭太多连接(我知道连接池,从池中查找连接仍然比它更昂贵重用一个对象)

所以我最终得到了这些方面的东西(完全未经测试):

class DatabaseContext : IDisposable {

    List<DatabaseContext> currentContexts;
    SqlConnection connection;
    bool first = false; 

    DatabaseContext (List<DatabaseContext> contexts)
    {
        currentContexts = contexts;
        if (contexts.Count == 0)
        {
            connection = new SqlConnection(); // fill in info 
            connection.Open();
            first = true;
        }
        else
        {
            connection = contexts.First().connection;
        }

        contexts.Add(this);
    }

   static List<DatabaseContext> DatabaseContexts {
        get
        {
            var contexts = CallContext.GetData("contexts") as List<DatabaseContext>;
            if (contexts == null)
            {
                contexts = new List<DatabaseContext>();
                CallContext.SetData("contexts", contexts);
            }
            return contexts;
        }
    }

    public static DatabaseContext GetOpenConnection() 
    {
        return new DatabaseContext(DatabaseContexts);
    }


    public SqlCommand CreateCommand(string sql)
    {
        var cmd = new SqlCommand(sql);
        cmd.Connection = connection;
        return cmd;
    }

    public void Dispose()
    {
        if (first)
        {
            connection.Close();
        }
        currentContexts.Remove(this);
    }
}



void Test()
{
    // connection is opened here
    using (var ctx = DatabaseContext.GetOpenConnection())
    {
        using (var cmd = ctx.CreateCommand("select 1"))
        {
            cmd.ExecuteNonQuery(); 
        }

        Test2(); 
    }
    // closed after dispose
}

void Test2()
{
    // reuse existing connection 
    using (var ctx = DatabaseContext.GetOpenConnection())
    {
        using (var cmd = ctx.CreateCommand("select 2"))
        {
            cmd.ExecuteNonQuery();
        }
    }
    // leaves connection open
}
于 2008-10-30T20:52:16.333 回答
8

出于自动化测试的目的,传递它通常更容易。这称为依赖注入

当您需要编写测试时,您可以创建一个模拟数据库连接对象并传递它而不是真实的。这样,您的自动化测试就不会依赖于每次都需要重新填充数据的实际数据库。

于 2008-10-30T20:21:38.593 回答
1

我个人努力尽可能集中我的数据访问,但是,如果不可能,我总是在其他类中打开一个新连接,因为我发现在传递实际连接时有太多其他事情可能会阻碍目的。

于 2008-10-30T20:20:05.977 回答
1

建立连接可能会很昂贵,并且可能会增加往返行程。所以,再一次,更好的设计是传递连接对象。

我说可能,因为如果您是 Microsoft ADO 应用程序,您可能正在使用连接池......

于 2008-10-30T20:21:21.387 回答
1

这是对这个问题的更深入的了解。我有一个管理数据库连接的类,并且有 2 个实现接口的类。其中一个类用于 SQL,另一个用于 OLAP。管理器知道要使用哪个连接,因此它可以将确切的连接传递给类型,或者类型可以创建自己的连接。

于 2008-10-30T20:27:44.807 回答
1

您可以毫无问题地传递连接对象(例如,Microsoft Enterprise Library 允许在连接中传递静态方法调用),或者您可以在外部对其进行管理,这取决于您的设计,没有直接的技术权衡。

如果您的解决方案将被移植到其他数据库,请注意可移植性不要传递特定连接(这意味着不要传递您计划与其他数据库一起使用的 SqlConnection)

于 2008-10-30T20:31:47.747 回答
1

我建议您区分连接对象及其状态(打开、关闭)。

您可以有一个从 web.config 读取连接字符串的方法(或属性)。每次使用相同版本的连接字符串可确保您从连接池中受益。

当您需要打开连接时调用该方法。在最后一刻,设置完所有 SqlCommand 属性后,打开连接,使用它,然后关闭它。在 C# 中,您可以使用 using 语句来确保连接已关闭。如果没有,请务必在 finally 块中关闭连接。

于 2008-10-30T20:57:35.573 回答
0

我会使用 web.config

<configuration>
    <connectionStrings>
        <add name="conn1" providerName="System.Data.SqlClient" connectionString="string here" />
        <add name="conn2" providerName="System.Data.SqlClient" connectionString="string here" />
    </connectionStrings>
</configuration>

然后您可以从应用程序中的任何位置引用它

于 2008-10-30T20:20:57.037 回答