Search code examples
c#design-patternsdependency-injectioninversion-of-controlsimple-injector

How to configure Simple Injector to implement command handler pattern inside a context transaction


I'm trying to apply command handler pattern on a WPF Application.

I have some problem to understand how to configure the IoC container (in my case I use Simple Injector) to having this kind of functionality.

I want to execute a database command handler (that execute some operation on DbContext) and every command has to be wrapped inside a transaction created on the same context.

Something like this (It's only an example)


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;
            }
        }
    }
}

So basically i need to implement something like this by using 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
}

with simple injector or even another IOC container.

What i need it's to implement the command handler with the business case logic wrapped inside a single scoped DbContext. To better explain i need to get from the container the correct command handler that execute his logic inside a well defined Dbcontext that going to be disposed after the execution.

But i don't understand how to accomplish this by registering all the class in the ioc container. Maybe there is something wrong in my command handler design or use of DI.

Could you please give me some example or drive me in the correct way?

Thanks in advance


Solution

  • Simple Injector allows Auto-Registration of generic types, like your command handlers. Registering all your command handlers can be done with a single call to Container.Register:

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

    After that, you can register your decorators one by one:

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

    Do note that such decorator works best when it is a generic type, because in that case, Simple Injector can wrap it around any arbitrary IDatabaseCommand<,> implementation.

    For more information about registering generic types and decorators, read our fine documentation: