繁体   English   中英

带有 .NET Core 的 Dapper - 注入的 SqlConnection 生命周期/范围

[英]Dapper with .NET Core - injected SqlConnection lifetime/scope

我正在使用 .NET Core Dependency Injection 在应用程序启动期间实例化SqlConnection对象,然后我计划将其注入到我的存储库中。 Dapper 将使用此SqlConnection从我的存储库实现中的数据库读取/写入数据。 我将使用 Dapper 的async调用。

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

使用 Dapper 时,是否有关于 SqlConnection 对象生命周期的任何建议/最佳实践? 我可能会遗漏任何注意事项吗?

提前致谢。

如果您提供 SQL 连接作为单例,除非您启用 MARS,否则您将无法同时处理多个请求,这也有其局限性。 最佳做法是使用瞬态 SQL 连接并确保正确处置它。

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

我同意@Andrii Litvinov 的回答和评论。

在这种情况下,我将采用数据源特定连接工厂的方法。

使用相同的方法,我提到了不同的方式 - UnitOfWork。

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

  • UnitOfWork作为瞬态注入。
  • 可以通过为每个数据源创建单独的DalSession来处理多个数据源。
  • UnitOfWork注入BaseDal

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

大多数开发人员同意的一件事是,连接应该尽可能短。 我在这里看到两种方法:

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

很好的问题,已经有两个很好的答案。 一开始对这个很疑惑,想出了如下方案来解决这个问题,将repositories封装在一个manager中。 管理器本身负责提取连接字符串并将其注入存储库。

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

我要问自己的主要问题是 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());
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM