TLDR:是注入连接工厂而不是注入IDbConnection本身的原因是什么。
我目前正在.net MVC中使用Autofac将IDbConnection的实例注入到我的存储库类中,以便与Dapper一起使用,如下所示:
自动设置:
builder.Register<IDbConnection>(ctx => new
SqlConnection(conSettings.ConnectionString)).InstancePerRequest();
回购:
public ClientRepository(IDbConnection connection)
{
_connection = connection;
}
public async Task<IEnumerable<Client>> GetAsync()
{
string query = "SELECT * FROM Clients";
return (await _connection.QueryAsync<Client>(query)).ToList();
}
到目前为止,这对我来说一直很好,但是我有点担心连接保持打开状态而不被丢弃。
我在该主题上发现的每一篇帖子都以某人建议传递一个连接工厂并在using语句中调用它为结尾,而没有真正提及为什么我当前的设置是“错误的”。
据我所知,每个请求都应该获得自己的IDbConnection,Dapper负责打开和关闭连接,而Autofac负责处理。
不是这样吗?我想念什么吗?
答案 0 :(得分:3)
他们以这种方式在ASP.NET Core项目上做到这一点(请耐心等待我一秒钟,我知道这不是您正在使用的东西,但该概念仍然适用)是通过存储库构造函数注入连接字符串。
您将看到,实际上是注入IConfiguration
对象,因为由于其他要求,我需要配置文件中的其他设置。只是假装是连接字符串。
然后我的存储库看起来像这样(一个粗略的例子,写在我的头顶上,请原谅我可能犯的任何错误):
public class FooRepository
{
private readonly IConfiguration _configuration;
public FooRepository(IConfiguration configuration)
{
_configuration = configuration
}
private IDbConnection Connection => new SqlConnection(_configuration.GetConnectionString("myConnectionString"));
public Foo GetById(int id)
{
using (var connection = Connection)
{
return connection.QueryFirstOrDefault<Foo>("select * from ...", new {id});
}
}
}
将ADO.NET连接池化,根据需要打开一个连接,然后按通常的方式关闭它。使用using
,您可以确保连接一旦完成就立即关闭并释放-返回到池中-即使抛出异常。
当然,您可能希望将此通用代码提取到抽象超类中,这样就无需在每个存储库中重复连接字符串的名称,也不需要重新实现Connection
属性。 / p>
此外,正如我在评论中提到的那样,Dapper并不负责打开或关闭连接,实际上,它完全希望在调用任何方法之前打开连接。 < strong>对不起,这不再是事实。
答案 1 :(得分:0)
如果仅注入IDbConnection,则意味着您的存储库只能使用该连接,并且您依赖IoC为您关闭/处置该连接。另外,如果您需要连接到两个不同的数据库,则不能,因为您只允许在此处创建一个连接。如果要并行运行查询,则不能,因为一次只能对一个数据库进行一次打开调用。最后,如果获取连接字符串有点困难,而且不是直接来自配置文件(例如KeyVault),那么您需要调用外部Async方法或IoC可能不允许您执行的操作。
对我来说,我一直使用工厂,因为我想在完成连接后立即关闭任何连接,而不是等待IoC摆脱它。 (允许存储库外部的某些内容管理数据库连接感觉很脏。)我想控制要连接到的数据库(我经常需要处理多个数据库)。为了返回我需要的所有数据,有时我需要并行运行一堆不同的查询,因此我需要在一个方法中进行多个连接。我还必须做一些逻辑,因为我们将连接字符串存储在Azure Key Vault中,因此我必须进行异步调用以获取包含机密信息的信息,这有点复杂,因此工厂中的Create方法最终会执行很多工作。