Как настроить простой инжектор для реализации шаблона обработчика команд внутри контекстной транзакции

#c# #design-patterns #dependency-injection #inversion-of-control #simple-injector

#c# #шаблоны проектирования #зависимость-внедрение #инверсия управления #простой инжектор

Вопрос:

Я пытаюсь применить шаблон обработчика команд к приложению WPF.

У меня есть некоторые проблемы с пониманием того, как настроить контейнер IoC (в моем случае я использую Simple Injector) для обеспечения такой функциональности.

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

Что-то вроде этого (это только пример)

 
public class BusinessUseCases
{
    public void BusinessCase1()
    {
        BusinessCommandParams1 commandParams = new BusinessCommandParams1();

        using (var db = new DbContext(connectionString))
        {
            IDatabaseCommand<DatabaseResult, BusinessCommandParams1> databaseCreate =
                new TransactionDatabaseCommandDecorator(new BusinessCommand(db), db);

            databaseCreate.Execute(commandParams);
        }
    }

    ....

    Other business case
}

public interface IDatabaseCommand<TResult, TParam>
{
    TResult Execute(TParam commandParam);
}

public class BusinessCommandParams1
{
    //some property
}

public class DatabaseResult
{
    //some property
}
public class BusinessCommand : IDatabaseCommand<DatabaseResult, BusinessCommandParams1>
{
    private readonly DbContext _context;

    public BusinessCommand(IDbContext context)
    {
        _context = context;
    }

    public DatabaseResult Execute(BusinessCommandParams1 commandParam)
    {
        //use context
        return new DatabaseResult();
    }
}

public class TransactionDatabaseCommandDecorator : IDatabaseCommand<DatabaseResult, BusinessCommandParams1>
{
    private readonly IDatabaseCommand<DatabaseResult, BusinessCommandParams1> _command;
    private readonly DbContext _context;

    public TransactionDatabaseCommandDecorator(IDatabaseCommand<DatabaseResult, BusinessCommandParams1> command, DbContext context)
    {
        _command = command;
        _context = context;
    }

    public DatabaseResult Execute(BusinessCommandParams1 commandParam)
    {
        using (var transaction = _context.Database.BeginTransaction())
        {
            try
            {
                _command.Execute(commandParam);
                transaction.Commit();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                transaction.Discard();
                throw;
            }
        }
    }
}
  

Итак, в основном мне нужно реализовать что-то подобное, используя POOR MAN DI

 public class BusinessUseCases
{
    public void BusinessCase1()
    {
        BusinessCommandParams1 commandParams = new BusinessCommandParams1();


        using (var db = new DbContext(connectionString))
        {
            IDatabaseCommand<DatabaseResult, BusinessCommandParams1> databaseCreate =
                new TransactionDatabaseCommandDecorator(new BusinessCommand(db), db);

            databaseCreate.Execute(commandParams);
        }
    }

    ....

    Other business case
}
  

с помощью простого инжектора или даже другого контейнера IOC.

Что мне нужно, так это реализовать обработчик команд с логикой бизнес-кейса, заключенной в один ограниченный DbContext. Чтобы лучше объяснить, мне нужно получить из контейнера правильный обработчик команд, который выполняет его логику внутри четко определенного Dbcontext, который будет удален после выполнения.

Но я не понимаю, как это сделать, зарегистрировав весь класс в контейнере ioc. Возможно, что-то не так в моем дизайне обработчика команд или использовании DI.

Не могли бы вы привести мне какой-нибудь пример или указать правильный путь?

Заранее спасибо

Ответ №1:

Simple Injector позволяет автоматически регистрировать общие типы, такие как ваши обработчики команд. Регистрация всех ваших обработчиков команд может быть выполнена с помощью одного вызова Container.Register :

 container.Register(typeof(IDatabaseCommand<,>), typeof(BusinessCommand).Assembly);
  

После этого вы можете зарегистрировать свои декораторы один за другим:

 container.RegisterDecorator(
    typeof(IDatabaseCommand<,>),
    typeof(TransactionDatabaseCommandDecorator));
  

Обратите внимание, что такой декоратор работает лучше всего, когда он является универсальным типом, потому что в этом случае простой инжектор может обернуть его вокруг любой произвольной IDatabaseCommand<,> реализации.

Для получения дополнительной информации о регистрации универсальных типов и декораторов ознакомьтесь с нашей прекрасной документацией:

Комментарии:

1. Спасибо @Steven, это очень полезно, и я попытаюсь это реализовать. Но чего я не понимаю, так это как на самом деле внедрить команду в класс BusinessCase. Если я использую внедрение конструктора, мне нужно зарегистрировать даже DbContext, но если я введу это, как я контролирую время жизни dbcontext?

2. Что мне нужно, так это иметь один ограниченный Dbcontext для каждого бизнес-кейса, реализованного обработчиком команд …. не могли бы вы привести мне какой-нибудь пример этого?

3. Взгляните на ScopedCommandHandlerProxy<T> в этом разделе документации.

4. Спасибо, это то, что я ищу. Большое вам спасибо!!!!!