我有两个装饰器:
class DbCommandWithTransactionHandlerDecorator<TCommand>
: IDbCommandHandler<TCommand> { ... }
class DbOptimisticConcurrencyRetryDecorator<TCommand>
: IDbCommandHandler<TCommand> { ... }
这些装饰器为数据库命令添加了事务管理和乐观并发重试功能。
我使用Autofac作为我的IoC容器。我想设置Autofac,它会自动连接程序集中找到的所有IDbCommandHandler<>
,这样当我请求说IDbCommandHandler<CreateNewNotificationCommand>
时,它会自动装饰它DbCommandWithTransactionHandlerDecorator
,然后是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
答案 0 :(得分:3)
我确实设法通过反射找到了一种解决方法,虽然它的工作原理并不是很优雅。为了完整起见,我将在下面发布:
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>>();
}
首先,我通过反思了所有实现IDbCommandHandler
的类型。然后,我将它们注册为它们实现的所有接口的命名类型,给它们命名为“dbCommand”。
然后,我注册通用装饰器来装饰名为'dbCommand'的类型。这个decorater名为'dbCommandWithTransaction',然后用于为并发重试注册另一个通用装饰器。
考虑到这是可以做一次并且“忘记”的事情,我已经准备好了解这个解决方法。但是,我正在尝试其他IoC容器并且遇到了Simple Injector,所有这些只需要两行代码即可完成 - 从那时起我就赢了。