关于C#:带有.NET Core的Dapper-注入的SqlConnection生命周期/作用域

Dapper with .NET Core - injected SqlConnection lifetime/scope

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

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

使用Dapper时,是否有关于SqlConnection对象生存期的任何建议/最佳实践? 有什么需要注意的地方吗?

提前致谢。


如果您以单例方式提供SQL连接,则除非启用MARS,否则将无法同时处理多个请求,这也有其局限性。最佳实践是使用临时SQL连接并确保正确处理它。

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


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

In this case I would go with approach of data-source specific connection factory.

使用相同的方法,我要提到的是不同的方法-UnitOfWork。

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

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

Are there any recommendations/best practices regarding the lifetime of the SqlConnection object when working with Dapper?

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

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

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

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

    我要问自己的主要问题是DbService是否应为单例。我的理由是,不断创建和销毁封装在DbService中的各种存储库没有什么意义,而且由于它们都是无状态的,因此我认为允许它们"存在"没有太大问题。虽然这可能是完全无效的逻辑。

    EDIT: Should you want a ready made solution check out my Dapper repository implementation on GitHub

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    /*
     * 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;
            }
        }
    }

    样本存储库的结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /*
     * 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
        }
    }

    全部接线:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
     * Startup.cs
     */

    public IConfigurationRoot Configuration { get; }

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

        services.AddSingleton<IDbService, DbService>();

        //...rest of services
    }

    最后,使用它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public SomeController : Controller
    {
        IDbService dbService;

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

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