在 DAL 中处理数据库连接的最佳方法 - 创建还是通过?

Posted

技术标签:

【中文标题】在 DAL 中处理数据库连接的最佳方法 - 创建还是通过?【英文标题】:Best way to deal with DB Connection in the DAL - create or pass? 【发布时间】:2011-06-16 03:36:27 【问题描述】:

我希望我的数据访问层构建得非常模块化。 因此,我有数据检索方法,有时直接从业务层调用,有时由其他数据检索方法调用以创建对象依赖关系。

在 DAL 中处理数据库连接的最佳方式是什么?

a) 在每个方法中创建一个新的连接,然后再处理它。好:易于编写和使用。不好: 许多连接正在打开和关闭。 (性能?)

b) 将连接作为(可选)参数传递。很好:我可以为多个命令重用打开的连接。不好:我必须跟踪连接的所有权(谁必须关闭它?)并且不能使用非常简洁的“使用”语句。

c) 别的? (可能是单例连接?)

这是我第一次编写真正的 DAL,所以我真的可以向各位有经验的人寻求帮助。

编辑:看起来很重要,这是一个 ASP.Net 网站项目。

【问题讨论】:

可能是切线的,但 FWIW,我宁愿使用 nhibernate 并让它处理这一切。即只使用 nhibernate 提供的会话对象。 【参考方案1】:

如果您使用的是 ASP.Net,选项 A 是您的朋友。

为每个请求创建一个新连接,当请求完成时 Dispose()-ing。确保您使用 相同 连接字符串。连接将(默认情况下)保持打开状态并通过连接池可用。

有关连接池的更多信息,请参阅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 网站。感谢您的链接和答案。但我不明白传递连接如何成为并发问题? @atticae:如果连接对象最终被多个线程使用,它只会成为并发问题。是否会发生这种情况取决于您的代码的结构,但如果您在数据访问方法的上下文中创建和处理连接,那么意外发生的可能性要小得多。 您可能想要传递 SqlConnection 的唯一时间是多个 DAL 方法需要参与同一个 SqlTransaction。这可能是最好避免的。【参考方案2】:

最好的选择是有一个连接池,您可以在其中获取连接。让连接池处理连接的生命周期,并在需要时获取已经打开的连接! 这方面的一个例子是http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx

【讨论】:

【参考方案3】:

无论如何,大多数提供程序都会在 ODBC 级别或 .NET 级别共享连接。所以选项 A 更安全,而且在性能方面可能同样出色。

见:http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx

还有:http://msdn.microsoft.com/en-us/library/ms254502.aspx

【讨论】:

【参考方案4】:

全部使用三个

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

如果传入一个连接,假设它已经打开,不要关闭它。在更高的层次上,当你调用这个方法时,你可以使用 using 语句来处理连接的关闭。

【讨论】:

【参考方案5】:

我们使用选项 A 的变体。

我们实际上使用实体框架,以便我们可以利用 LINQ 等。实体框架管理自己的连接池,因此创建和删除上下文很便宜。然后我们利用依赖注入来管理连接的实际创建,如下所示:

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().

【讨论】:

以上是关于在 DAL 中处理数据库连接的最佳方法 - 创建还是通过?的主要内容,如果未能解决你的问题,请参考以下文章

制作 DAL 的最佳方法是啥?

使用 ADO.NET 为 MVC 3 应用程序设计 DAL 的最佳方法?

在 WPF、Silverlight 和 ASP.NET 之间共享一个公共 DAL

.net编程中BLL/DAL/Model等这些层是干啥的,怎样实现他们的相互连接!

多租户隔离数据库上 DAL 和配置的最佳实践

n 层架构 - BLL、DAL 和接口。啥是最佳实践?