在数据访问层(DAL)中处理数据库连接的最佳方法是创建还是传递?

5
我希望我的数据访问层非常模块化。因此,我有一些数据检索方法,有时直接从业务层调用,有时被其他数据检索方法调用以创建对象依赖关系。
在DAL中处理数据库连接的最佳方法是什么?
a) 在每个方法中创建一个新连接并在之后释放它。
优点:易于编写和使用。
缺点:会打开和关闭很多连接。(性能问题?)
b) 将连接作为(可选)参数传递。
优点:可以重复使用打开的连接进行多个命令。
缺点:必须跟踪连接的所有权(谁必须关闭它?),不能使用非常方便的“using”语句。
c) 其他方法?(例如将连接作为单例?)
这是我第一次编写真正的DAL,所以我确实需要一些来自有经验的人的帮助。
编辑:似乎很重要,这是一个ASP.Net网站项目。

可能有些离题,但是就我而言,我宁愿使用nhibernate并让它处理所有这些。也就是说,只需使用nhibernate提供的会话对象即可。 - Biswanath
5个回答

4
如果您正在使用ASP.Net,则选项A是您的朋友。
为每个请求创建一个新连接,在请求完成时Dispose()。确保使用相同的连接字符串。这些连接(默认情况下)将保持打开状态,并通过连接池可用。
有关连接池的更多信息,请参见http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx
在Web服务器中,您基本上必须以这种方式执行操作,以便不会出现并发问题。一切都需要是线程安全的(您不知道有多少并发工作线程正在执行您的应用程序)。
[添加示例代码]
例如,以下是我认为是执行存储过程的典型方法。这来自我编写的自定义代码生成器 - 手写代码可能看起来有些不同 - 但它应该足以传达要点:
public int Exec(  int? @iPatientID )
{
  using ( SqlConnection  conn = new SqlConnection( this.ConnectString ) )
  using ( SqlCommand     cmd  = conn.CreateCommand() )
  using ( SqlDataAdapter sda  = new SqlDataAdapter( cmd ) )
  {
    cmd.CommandText = STORED_PROCEDURE_NAME ;
    cmd.CommandType = CommandType.StoredProcedure ;

    if ( this.TimeoutInSeconds.HasValue )
    {
      cmd.CommandTimeout = this.TimeoutInSeconds.Value ;
    }

    //
    // 1. @iPatientID
    //
    SqlParameter p1 = new SqlParameter( @"@iPatientID" , SqlDbType.Int ) ;
    if ( @iPatientID == null )
    {
      p1.Value = System.DBNull.Value ;
    }
    else
    {
      p1.Value = @iPatientID ;
    }
    cmd.Parameters.Add( p1 ) ;

    // add return code parameter
    SqlParameter pReturnCode = new SqlParameter() ;
    pReturnCode.SqlDbType    = System.Data.SqlDbType.Int ;
    pReturnCode.Direction    = System.Data.ParameterDirection.ReturnValue ;
    cmd.Parameters.Add( pReturnCode ) ;

    DataSet ds = new DataSet() ;

    conn.Open() ;
    sda.Fill( ds ) ;
    conn.Close() ;

    this.ResultSet  = ( ds.Tables.Count > 0 ? ds.Tables[0] : null ) ;
    this.ReturnCode = (int) pReturnCode.Value ;

  }

  return this.ReturnCode ;

}

是的,这是一个ASP.Net网站。感谢您提供链接和答案。但我不明白如何传递连接可能会成为并发问题? - magnattic
@atticae:只有当连接对象被多个线程使用时,才会出现并发问题。这取决于您的代码结构,但如果您在数据访问方法的上下文中创建和处理连接,则意外发生的可能性要小得多。 - StriplingWarrior
1
唯一需要传递SqlConnection的时间是如果多个DAL方法需要参与相同的SqlTransaction。这可能是最好避免的。 - user287466

2

我们使用A选项的一种变体。

实际上,我们使用Entity Framework来利用LINQ等功能。Entity Framework管理自己的连接池,因此创建和删除上下文是廉价的。然后,我们利用依赖注入来管理连接的实际创建,如下所示:

public class MyDao 
{
    IFactory<MyDataContext> _contextFactory;
    public MyDao(IFactory<MyDataContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }

    public Foo GetFooById(int fooId)
    {
        using (var context = _contextFactory.Get())
        {
            return context.Foos.Single(f => f.FooId == fooId);
        }
    }
}

这样,如果我们决定使用不同的连接字符串或者更复杂的方法来创建上下文,我们只需要在一个地方更改依赖注入绑定,而不是寻找每个调用 new MyDataContext()


1

1

1

使用全部三个

将连接作为可选参数编写。如果传入的是none(null),则从某些(单例,可能)共享源创建连接,以便所有DAL类都使用完全相同的连接字符串创建连接(如@Nicholas Carey所述的池化)。仅在创建时打开和关闭它。

如果传入了连接,请假定它已经打开,并且不要关闭它。在更高级别上,当您调用此方法时,可以使用using语句来处理关闭连接。


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