Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windsor Castle/ DI and object models

I use Windsor Castle quite a while now. It is just perfect for environments where data is located in a database or the like, where the repository pattern or the unitofwork pattern do work well.

Now I do have a different situation: I have a complex object model that is assembled by a lot of single PONOs. The environment is strongly influenced by COM, to make it more explicit: Excel, Word PIOs are heavily used.

I do use the command pattern, I implemented the ICommandHandler like described here but with one difference. As I do want to assembly commands to a list of commands to call them in a run without knowing anything beside the general command pattern described , it does not make to introduce the context when calling the execute method. So the interface does look like this:

    public interface ICommand
    {
        void Execute();
        bool CanExecute();
    }

Execution of commands with that interface is effective and easy to understand. On the other hand it is a problem to introduce the context with the ctor because therefore the Container must be called explicitely to e.g. add ctor parameters.

So I actually have two questions:

  1. Is it possible to inject a - let's call it a context, a part of the object model - automatically by Windsor castle without calling the container explictely?
  2. How to participate from the command pattern by using DI? Any ideas how to accomplish being able to define a list of tasks/ actions or the like by following the RRR rule described here?
like image 894
Holger Leichsenring Avatar asked Oct 04 '22 19:10

Holger Leichsenring


1 Answers

Infrastructure:

public interface ICommandHandler<in T>
{
    void Handle(T command);
}

public interface ICommandExecutor
{
    CommandResult ExecuteCommand(Command command);
    CommandResult ExecuteCommands(Command[] commands);
}

public abstract class Command
{

}

public class CommandExecutor : ICommandExecutor
{
    private readonly IWindsorContainer _kernel;

    public CommandExecutor(IWindsorContainer kernel)
    {
        Guard.AssertNotNull(() => kernel);
        _kernel = kernel;
    }

    public CommandResult ExecuteCommand(Command command)
    {
        return ExecuteInternal(command);
    }

    public CommandResult ExecuteCommands(Command[] commands)
    {
        CommandResult result = null;

        foreach (Command command in commands)
        {
            result = ExecuteInternal(command);

            if (!result.IsExecuted)
                return result;
        }

        return result ?? CommandResult.Executed("Command executed successfully");
    }

    private CommandResult ExecuteInternal(Command command)
    {
        dynamic handler = FindHandlerForCommand(command);

        try
        {
            handler.Handle(command as dynamic);
            return CommandResult.Executed("Command executed successfully");
        }
        finally
        {
            _kernel.Release(handler);
        }
    }

    private object FindHandlerForCommand(Command command)
    {
        Type handlerType = typeof (ICommandHandler<>).MakeGenericType(command.GetType());
        dynamic handler = _kernel.Resolve(handlerType);
        return handler;
    }
}

Registration:

        container.Register(Component.For<ICommandExecutor>().ImplementedBy<CommandExecutor>()
            .Interceptors<ExceptionToCommandResult>()
            .Interceptors<ExceptionLogger>()
            .Interceptors<HandleWhenDeadlockVictim>()
            .Interceptors<RetryCommand>()
            .Interceptors<ContainerScopeWrapper>()
            .Interceptors<TransactionWrapper>()
            .Interceptors<SameNhibernateSessionAndTransactionWrapper>());

Example:

public class WriteComment : Command
{
    public string GameToCommentId { get; set; }

    public string Comment { get; set; }
}

public class WriteCommentCommandHandler : ICommandHandler<WriteComment>
{
    private readonly IGameRepository _repository;

    public WriteCommentCommandHandler(IGameRepository repository)
    {
        Guard.AssertNotNull(() => repository);
        _repository = repository;
    }

    public void Handle(WriteComment command)
    {
        var game = _repository.Get(new Guid(command.GameToCommentId));

        game.WriteComment(command.Comment, DateTime.Now);
    }
}

All that AOP stuff handles transactions and so forth. The command executor is a stateless singleton, the handlers specify their different needs. The command executor is just infrastructure and domain-agnostic, so place it where you like outside of your domain. This is production code on a big system, works like a charm.

like image 75
Marius Avatar answered Oct 07 '22 17:10

Marius