#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. Спасибо, это то, что я ищу. Большое вам спасибо!!!!!