提问者:小点点

带有.NET核心注入的SqlConnection生存期/范围的Dapper


我在用。NET Core Dependency Injection在应用程序启动时实例化一个< code>SqlConnection对象,然后我计划将它注入到我的存储库中。Dapper将使用这个< code>SqlConnection从我的存储库实现中的数据库读取/写入数据。我将对Dapper使用< code>async调用。

问题是:我应该将< code>SqlConnection作为瞬态还是单例注入?考虑到我想使用< code>async这一事实,我的想法是使用transient,除非Dapper在内部实现了一些隔离容器,并且我的singleton的作用域仍将被包装在Dapper内部使用的任何作用域内。

在使用Dapper时,是否有关于SqlConnection对象生存期的建议/最佳实践?是否有我可能遗漏的警告?

提前感谢。


共3个答案

匿名用户

如果您以singleton的形式提供SQL连接,您将无法同时服务多个请求,除非您启用MARS,这也有其局限性。最佳实践是使用瞬态SQL连接,并确保正确处置它。

在我的应用程序中,我将自定义的< code > IDbConnectionFactory 传递给存储库,该存储库用于在< code>using语句中创建连接。在这种情况下,存储库本身可以是单独的,以减少堆上的分配。

匿名用户

很好的问题,已经有两个很好的答案。起初我对此感到困惑,并提出了以下解决方案来解决问题,它将存储库封装在管理器中。管理器本身负责提取连接字符串并将其注入存储库。

我发现这种方法可以单独测试存储库,比如在模拟控制台应用程序中,变得更加简单,而且我很幸运地在几个大型项目中遵循这种模式。虽然我承认我不是测试、依赖注入或任何事情的专家!

我留给自己的主要问题是,DbService是否应该是单例。我的理由是,不断创建和销毁封装在DbService中的各种存储库没有多大意义,因为它们都是无状态的,所以我认为允许它们“存在”没有太大问题。尽管这可能是完全无效的逻辑。

编辑:如果您想要现成的解决方案,请在GitHub上查看我的Dapper存储库实现

存储库管理器的结构如下:

/*
 * Db Service
 */
public interface IDbService
{
    ISomeRepo SomeRepo { get; }
}

public class DbService : IDbService
{
    readonly string connStr;
    ISomeRepo someRepo;

    public DbService(string connStr)
    {
        this.connStr = connStr;
    }

    public ISomeRepo SomeRepo
    {
        get
        {
            if (someRepo == null)
            {
                someRepo = new SomeRepo(this.connStr);
            }

            return someRepo;
        }
    }
}

示例存储库的结构如下:

/*
 * Mock Repo
 */
public interface ISomeRepo
{
    IEnumerable<SomeModel> List();
}

public class SomeRepo : ISomeRepo
{
    readonly string connStr;

    public SomeRepo(string connStr)
    {
        this.connStr = connStr;
    }

    public IEnumerable<SomeModel> List()
    {
        //work to return list of SomeModel 
    }
}

将其连接起来:

/*
 * Startup.cs
 */
public IConfigurationRoot Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    //...rest of services

    services.AddSingleton<IDbService, DbService>();

    //...rest of services
}

最后,使用它:

public SomeController : Controller 
{
    IDbService dbService;

    public SomeController(IDbService dbService)
    {
        this.dbService = dbService;
    }

    public IActionResult Index()
    {
        return View(dbService.SomeRepo.List());
    }
}

匿名用户

我同意@Andrii Litvinov的观点,无论是回答还是评论。

在本例中,我将采用数据源特定连接工厂的方法。

用同样的方法,我提到了不同的方法——工作单元。

从这个答案中参考DalSessionUnitOfWork。这将处理连接。
从这个答案中参考 BaseDal。这是我对Repository(实际上是BaseRepository)的实现。

  • UnitOfWork作为瞬态注入
  • 通过为每个数据源创建单独的<code>DalSession</code>,可以处理多个数据源
  • UnitOfWork被注入到BaseDal

在使用Dapper时,是否有关于SqlConnection对象生存期的建议/最佳实践?

大多数开发者都认为,连接应该尽可能短。我在这里看到了两种方法:

  1. 每个动作的连接
    这当然是连接的最短寿命。对于每个操作,使用块将连接括在中。只要您不想将动作分组,这是一个好方法。即使您想对操作进行分组,也可以在大多数情况下使用事务
    问题是当您想要跨多个类/方法对操作进行分组时。您不能在此处使用
    块。解决方案是UnitOfWork,如下所示
  2. 每个工作单元的连接
    定义工作单位。这将因应用而异。在web应用程序中,“按请求连接”是广泛使用的方法
    这更有意义,因为通常(大多数时候)我们都想作为一个整体执行一组动作。这在我上面提供的两个链接中进行了解释
    此方法的另一个优点是,应用程序(使用DAL)可以更好地控制如何使用连接。在我的理解中,应用程序比DAL更了解如何使用连接