我希望我的数据访问层非常模块化 因此,我有数据检索方法,有时直接从业务层调用,有时由其他数据检索方法调用,以创建对象依赖。
在DAL中处理数据库连接的最佳方法是什么?
a)在每种方法中创建一个新连接,然后再处理它。
好:易于编写和使用。
错误:正在打开和关闭许多连接。 (性能?)
b)将连接作为(可选)参数传递。
好:我可以为多个命令重用开放连接
坏:我必须跟踪连接的所有权(谁必须关闭它?)并且不能使用非常简洁的“使用”语句。
c)还有别的吗? (单身连接可能?)
这是我第一次写一个真正的DAL,所以我真的可以从经验丰富的人那里得到一些帮助。
编辑:因为它似乎很重要,它是一个ASP.Net网站项目。答案 0 :(得分: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 ;
}
答案 1 :(得分:2)
我们使用选项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()
。
答案 2 :(得分:1)
最好的选择是建立一个可以连接的连接池。让conenction池处理连接的生命周期,并在需要时获取已经打开的连接! 一个例子是http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx
答案 3 :(得分:1)
无论如何,大多数提供程序都会在ODBC级别或.NET级别上池连接。所以选项A既安全又可能同样表现良好。
答案 4 :(得分:1)
全部使用
将连接写为可选参数。如果传入none(null),则从某些(单个,可能)共享源创建连接,这样所有DAL类都创建具有相同精确连接字符串的连接(用于@Nicholas Carey所提到的池化)。只有在创建它时才打开和关闭它。
如果传入连接,则假定它已经打开,并且不要关闭它。在更高级别,当您调用此方法时,可以使用using语句来处理连接的关闭。