C# 将DB连接对象传递给方法

C# 将DB连接对象传递给方法,c#,.net,ado.net,database-connection,C#,.net,Ado.net,Database Connection,我想知道是否建议将数据库连接对象传递(到其他模块)或让方法(在其他模块中)负责设置它。我倾向于让方法设置它,以便在使用它之前不必检查连接的状态,并且让调用方将设置连接所需的任何数据传递给调用方法 我个人尽可能地集中我的数据访问,但是,如果不可能,我总是在其他类中打开一个新连接,因为我发现在传递实际的连接对象时,有太多其他东西可能会妨碍我。我会使用web.config <configuration> <connectionStrings> <a

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

我个人尽可能地集中我的数据访问,但是,如果不可能,我总是在其他类中打开一个新连接,因为我发现在传递实际的连接对象时,有太多其他东西可能会妨碍我。

我会使用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>


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

设置连接可能会很昂贵,并且可能会增加往返。因此,可能更好的设计是传递连接对象


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

出于自动测试的目的,通常更容易将其传入。这就是所谓的


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

下面是对这个问题的进一步了解。我有一个管理db连接的类,还有两个实现接口的类。其中一个类用于SQL,另一个是OLAP。管理器知道要使用哪个连接,因此它可以将确切的连接传递给该类型,或者该类型可以创建自己的连接。

您可以毫无问题地传递连接对象(例如,Microsoft Enterprise Library允许静态方法调用传入连接)或者,您可以根据自己的设计从外部对其进行管理,没有直接的技术权衡


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

我个人喜欢使用范围严格的连接;稍后打开它们,使用它们,然后关闭它们(在“使用”块中,全部在本地方法中)。在大多数情况下,连接池将处理重复使用连接的问题,因此这种方法没有实际的开销

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


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

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

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

因此,我最终得到了这些东西(完全未经测试):

类数据库上下文:IDisposable{
列出当前上下文;
SqlConnection连接;
bool first=false;
数据库上下文(列表上下文)
{
当前上下文=上下文;
如果(contexts.Count==0)
{
connection=new SqlConnection();//填写信息
connection.Open();
第一个=正确;
}
其他的
{
connection=contexts.First().connection;
}
上下文。添加(此);
}
静态列表数据库上下文{
得到
{
var contexts=CallContext.GetData(“contexts”)作为列表;
if(上下文==null)
{
上下文=新列表();
SetData(“上下文”,上下文);
}
返回上下文;
}
}
公共静态数据库上下文GetOpenConnection()
{
返回新的DatabaseContext(DatabaseContext);
}
公共SqlCommand CreateCommand(字符串sql)
{
var cmd=新的SqlCommand(sql);
cmd.Connection=连接;
返回cmd;
}
公共空间处置()
{
如果(第一)
{
connection.Close();
}
当前上下文。删除(此);
}
}
无效测试()
{
//连接在此打开
使用(var ctx=DatabaseContext.GetOpenConnection())
{
使用(var cmd=ctx.CreateCommand(“选择1”))
{
cmd.ExecuteNonQuery();
}
Test2();
}
//处理后关闭
}
void Test2()
{
//重用现有连接
使用(var ctx=DatabaseContext.GetOpenConnection())
{
使用(var cmd=ctx.CreateCommand(“选择2”))
{
cmd.ExecuteNonQuery();
}
}
//使连接保持打开状态
}

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

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

当需要打开连接时调用该方法。在
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
}