2

我有两个装饰器:

class DbCommandWithTransactionHandlerDecorator<TCommand>
    : IDbCommandHandler<TCommand> { ... }

class DbOptimisticConcurrencyRetryDecorator<TCommand>
    : IDbCommandHandler<TCommand> { ... }

这些装饰器将事务管理和乐观并发重试功能添加到数据库命令。

我使用 Autofac 作为我的 IoC 容器。我想设置 Autofac 以便它自动连接所有IDbCommandHandler<>在程序集中找到的东西,这样当我请求说 an 时IDbCommandHandler<CreateNewNotificationCommand>,它会自动先用 a 装饰它DbCommandWithTransactionHandlerDecorator,然后用a 装饰它DbOptimisticConcurrencyRetryDecorator

我一直在尝试用 Autofac 来解决这个问题builder.RegisterGenericDecorator(),但还没有成功。主要问题是装饰器需要一个“命名”参数才能工作。下面是最“接近”我想要实现的示例代码 - 但是主要缺陷是我仍然必须手动注册类型。

var builder = new ContainerBuilder();
var a = Assembly.GetExecutingAssembly();

// I need to find a way how these can be 'auto-wired', 
// rather than having to manually wire each command.

builder.RegisterType<CreateNewNotificationCommandHandler>()
    .Named<IDbCommandHandler<CreateNewNotificationCommand>>("command");
builder.RegisterType<CreateNewNotificationCommandHandler_2>()
    .Named<IDbCommandHandler<CreateNewNotificationCommand_2>>("command");

builder.RegisterGenericDecorator(
    typeof(DbCommandWithTransactionHandlerDecorator<>),
    typeof(IDbCommandHandler<>),
    fromKey: "command");

var container = builder.Build();
var handler1 =
  container.Resolve<IDbCommandHandler<CreateNewNotificationCommand>>();
var handler2 =
  container.Resolve<IDbCommandHandler<CreateNewNotificationCommand_2>>();
handler1.Handle(null); //these are correctly decorated
handler2.Handle(null); //these are correctly decorated
4

1 回答 1

3

I did manage to find a workaround via reflection, which although it works is not really elegant. I will post it below for completeness sake:

 public interface IDbCommandHandler<in TCommand>: IDbCommandHandlerStub
    where TCommand : IDbCommand
{
    void Handle(TCommand command);
}

public interface IDbCommandHandlerStub
{

}

    private List<Type> getTypesThatImplementIDbCommandHandler(IEnumerable<Assembly> assemblyList)
    {
        List<Type> list = new List<Type>();
        foreach (var a in assemblyList)
        {
            var matches = a.GetTypes().Where(t => typeof(IDbCommandHandlerStub).IsAssignableFrom(t));
            list.AddRange(matches);
        }
        return list;
    }

private void registerDbCommands(List<Type> dbCommandHandlerTypes, ContainerBuilder builder)
    {
        foreach (var t in dbCommandHandlerTypes)
        {
            var interfaces = t.GetInterfaces();
            foreach (var i in interfaces)
            {
                builder.RegisterType(t).Named("dbCommand", i);
            }

        }
    }


   public void Test1()
   {
        ContainerBuilder builder = new ContainerBuilder();
        var dbCommandHandlerTypes = getTypesThatImplementIDbCommandHandler(assemblies);
        registerDbCommands(dbCommandHandlerTypes, builder);            

        builder.RegisterGenericDecorator(typeof(DbCommandWithTransactionHandlerDecorator<>),
                                        typeof(IDbCommandHandler<>),
                                        fromKey: "dbCommand", toKey:"dbCommandWithTransaction").SingleInstance();

        builder.RegisterGenericDecorator(typeof(DbOptimisticConcurrencyRetryDecorator<>),
                                        typeof(IDbCommandHandler<>),
                                        fromKey: "dbCommandWithTransaction").SingleInstance();

        var container = builder.Build();
        var handler1 = container.Resolve<IDbCommandHandler<CreateNewNotificationCommand>>();

}

First, I get via reflection all the types that implement IDbCommandHandler. Then, I register them as named types for all the interfaces which they implement, giving them a name of 'dbCommand'.

Then, I register the generic decorator to decorate types named 'dbCommand'. This decorater is named 'dbCommandWithTransaction', and is used to then register another generic decorator for the concurrency-retry.

Considering this is something which would be done once and 'forgotten', I was ready go with this workaround. However, I was trying out other IoC containers and came upon Simple Injector, and all this can be done in just two lines of code - And have since then won me over.

于 2012-10-18T15:24:10.493 回答