Какой хороший шаблон для создания DbContext, когда динамические данные определяют строку подключения - PullRequest
0 голосов
/ 31 мая 2019

Я получаю набор данных из другого веб-API. Может содержать несколько записей от разных клиентов. Я должен преобразовать эти данные и записать их в базу данных клиента. У каждого клиента есть своя база данных. Схема клиентских баз точно такая же.

Я создал фабрику DbContext, которая создает новый экземпляр DbContext на основе клиента, над которым сейчас работает приложение.

public ClientDbContext CreateClientDbContext(string clientNumber)
{
      var optionsBuilder = new DbContextOptionsBuilder<ClientDbContext>();
      var clientConnection = string.Format(_configuration.GetConnectionString("clientConnection"), clientNumber);
      optionsBuilder.UseSqlServer(clientConnection);

      clientDbContext clientDbContext = new ClientDbContext(optionsBuilder.Options);

      return clientDbContext;
}

Я использую фабрику таким образом:

foreach (var case in caseList)
{
    var clientDbContext = await _clientDbContextFactory.CreateClientDbContext(case.ClientNumber);
    _clientRepository = new ClientRepository(clientDbContext);
    var updatedCase = /// transform case here
    await _clientRepository.CreateCases(updatedCase);
}

Есть ли оптимальный вариант для этого? Возможно, что несколько строк данных будут иметь одного и того же клиента, поэтому я хотел бы повторно использовать один и тот же ClientDbContext.

Ответы [ 2 ]

2 голосов
/ 31 мая 2019

Вы можете переместить вашу логику для создания ClientContext в другой ответственный класс (в соответствии с принципами SOLID), например, DbContextFactory, и хранить там созданные DbContext для каждого клиента.Например:

public class DbContextFactory
{
    private readonly IConfiguration _configuration;
    private readonly Dictionary<string, ClientDbContext> _clientContexts = new Dictionary<string, ClientDbContext>();

    public DbContextFactory(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public ClientDbContext GetOrCreateClientContext(string clientNumber)
    {
        // if you have context already created - return it
        if (_clientContexts.ContainsKey(clientNumber))
            return _clientContexts[clientNumber];

        var optionsBuilder = new DbContextOptionsBuilder<ClientDbContext>();
        var clientConnection = string.Format(_configuration.GetConnectionString("clientConnection"), clientNumber);
        optionsBuilder.UseSqlServer(clientConnection);

        var clientDbContext = new ClientDbContext(optionsBuilder.Options);
        _clientContexts[clientNumber] = clientDbContext;

        return clientDbContext;
    }
}

Затем в рабочем классе вы можете сгруппировать данные по ClientNumber, для каждого клиента создать (или получить уже созданный) DbContext и репозиторий, а затем выполнить обновление данных.

public class Worker
{
    private readonly DbContextFactory _factory;

    public Worker(DbContextFactory factory)
    {
        _factory = factory;
    }

    public async Task DoWorkAsync()
    {
        // group by ClientNumber
        var groupedCases = caseList.GroupBy(x => x.ClientNumber);

        foreach (var groupedCase in groupedCases)
        {
            // For each client create context and repository
            var clientContext = _factory.GetOrCreateClientContext(groupedCase.Key);
            var clientRepository = new ClientRepository(clientContext);

            foreach (var @case in groupedCases)
            {
                var updatedCase = // transform case here
                await clientRepository.CreateCases(updatedCase);
            }
        }
    }
}

Вы можете использовать внедрение зависимостей или просто создать эти предложения следующим образом:

var factory = new DbContextFactory(yourConfiguration);
var worker = new Worker(factory);
await worker.DoWorkAsync();
1 голос
/ 31 мая 2019

Обрабатывайте обращения партиями, так как это позволяет повторно использовать клиентское соединение, прежде чем переходить к следующему. Примерно так:

var batches = caseList.GroupBy(x => x.ClientNumber);

foreach (var batch in batches)
{
    var clientDbContext = await _clientDbContextFactory.CreateClientDbContext(batch.ClientNumber);
    _clientRepository = new ClientRepository(clientDbContext);

    foreach (var item in batch)
    {
        var updatedCase = /// transform case here
        await _clientRepository.CreateCases(updatedCase);
    }
}

Вы также можете извлечь выгоду из задания группы задач для пакета, но вы должны указать это, чтобы увидеть, что вы от него получите. Это может выглядеть примерно так:

var tasksInBatch = new List<Task>();
foreach (var item in batch)
{
    var updatedCase = /// transform case here
    tasksInBatch.Add(_clientRepository.CreateCases(updatedCase));
}

await Task.WhenAll(tasksInBatch);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...