Внедрение IDbConnection против IDbConnectionFactory - PullRequest
4 голосов
/ 15 апреля 2019

TLDR: Каковы причины внедрения фабрики соединений против самой IDbConnection.

В настоящее время я использую Autofac в .net MVC для добавления экземпляра IDbConnection в мои классы репозитория для использования с Dapper следующим образом:

Настройка автозапуска:

builder.Register<IDbConnection>(ctx => new
    SqlConnection(conSettings.ConnectionString)).InstancePerRequest();

Repo:

public ClientRepository(IDbConnection connection)
{
    _connection = connection;
}

public async Task<IEnumerable<Client>> GetAsync()
{
    string query = "SELECT * FROM Clients";
    return (await _connection.QueryAsync<Client>(query)).ToList();
}

До сих пор это работало для меня совершенно нормально, но меня немного беспокоит то, что соединения остаются открытыми и не удаляются.

Каждый пост, который я нахожу по теме, заканчивается тем, что кто-то предлагает передать фабрику соединений и назвать ее в операторе использования, не упоминая, почему моя текущая настройка «плохая».

Насколько я могу сказать, каждый запрос должен иметь свой собственный IDbConnection, где Dapper заботится об открытии и закрытии соединения, а Autofac заботится об утилизации.

Разве это не так? Я что-то упустил?

Ответы [ 2 ]

3 голосов
/ 15 апреля 2019

То, как я делаю это в проекте 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.

Кроме того, как я уже упоминал в своем комментарии, Даппер не отвечает за открытие или закрытие соединений, фактически он полностью ожидает открытия соединения до того, как вы сможете вызвать любой из его методов. Это больше не правда, извините .

0 голосов
/ 09 мая 2019

Если вы вводите только IDbConnection, это означает, что ваш репозиторий может использовать только это одно соединение, и вы полагаетесь на IoC, чтобы закрыть / утилизировать это соединение для вас. Кроме того, если вам нужно подключиться к двум разным базам данных, вы не можете этого сделать, поскольку здесь разрешено создавать только одно соединение. Если вы хотите выполнять запросы параллельно, вы не можете этого сделать, поскольку вы можете одновременно иметь только один открытый вызов для одной базы данных. Наконец, если получить строку подключения немного сложнее и не прямо из файла конфигурации (например, KeyVault), то вам нужно вызвать внешний метод Async или что-то, что IoC не позволит вам сделать, вероятно.

Для меня я всегда использую фабрику, потому что я хочу закрыть любое соединение, как только я закончу с ним, вместо того, чтобы ждать, пока IoC избавится от него. (Мне кажется грязным разрешать что-то вне репозитория управлять соединениями с базой данных.) Я хочу контролировать, к какой базе данных я подключаюсь (у меня часто есть более 1 БД, с которыми мне приходится работать). Иногда мне нужно запускать несколько разных запросов параллельно, чтобы вернуть все нужные мне данные, поэтому мне нужно несколько соединений в одном методе. Я также должен сделать некоторую логику, так как мы храним наши строки подключения в хранилище ключей Azure, поэтому мне нужно сделать асинхронный вызов, чтобы получить это с секретной информацией, что немного усложняется, поэтому метод Create на фабрике в конечном итоге выполняет много работы.

...