我希望能够使用Unity Container,除了解析接口外,还可以包装实现公共父接口的所有实例。例如,假设您有为命令定义的公共接口以及处理它们的类的实现:
public interface ICommand {}
public interface ICommandHandler<T> where T : ICommand
{
void Execute(T command);
}
然后你有一些实现这些接口的类:
public class MoveCommand : ICommand { /* properties */ }
public class MoveHandler : ICommandHandler<MoveCommand>
{
public void Execute(MoveCommand command) { /* do stuff */ }
}
public class CreateCommand : ICommand { /* properties */ }
public class CreateHandler : ICommandHandler<CreateCommand>
{
public void Execute(CreateCommand command) { /* do other stuff */ }
}
然后在Unity Container中注册这些命令:
container.RegisterType<ICommandHandler<MoveCommand>, MoveHandler>(new ContainerControlledLifetimeManager());
container.RegisterType<ICommandHandler<CreateCommand>, CreateHandler>(new ContainerControlledLifetimeManager());
现在,假设您有一些用于实现跨领域问题的界面:
public interface ILogger<T> : ICommandHandler<T> where T : ICommand { }
public class Logger<T> : ILogger<T> where T : ICommand
{
private ICommandHandler<T> handler;
public Logger(ICommandHandler<T> handler)
{
this.handler = handler;
}
public void Execute(T command)
{
// Log stuff
handler.Execute(command);
}
}
在Unity中注册如下:
container.RegisterType(typeof(ILogger<>), typeof(Logger<>), new ContainerControlledLifetimeManager());
我希望能够在解析它时将Unity包装在ILogger中的每个ICommandHandler中。一种方法是修改每个ICommandHandler类型的RegisterType调用。但是,本着“不要重复自己”的精神,我真的希望能够指定一次所有 ICommandHandler类型应该另外包装在相应类型的ILogger中。可能会注册大量的ICommandHandler类型,以及错误处理,身份验证等等的其他包装器,因此重复和疏忽的机会将是巨大的。有没有办法立即将包装器应用于所有这些?
编辑:这是我正在寻找的语法,改编自接受的答案和接受的答案中的第一个链接:
container.RegisterType<ICommandHandler<MoveCommand>, MoveHandler>("InnerCommand", new ContainerControlledLifetimeManager());
container.RegisterType<ICommandHandler<CreateCommand>, CreateHandler>("InnerCommand", new ContainerControlledLifetimeManager());
container.RegisterType(typeof(ICommandHandler<>),
typeof(Logger<>),
InjectionConstructor(new ResolvedParameter(typeof(ICommandHandler<>), "InnerCommand")));
答案 0 :(得分:3)
您可以使用Unity拦截行为。您需要执行以下操作:
1)获取Unity.Interception NuGet包。
2)指示Unity容器使用拦截扩展名,如下所示:
container.AddNewExtension<Interception>();
3)创建这样的拦截行为:
public class LoggingBehavior : IInterceptionBehavior
{
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
var next_bahavior = getNext();
//Here do your logging before executing the method
var method_return = next_bahavior.Invoke(input, getNext);
//Here do your logging after executing the method
return method_return;
}
public IEnumerable<Type> GetRequiredInterfaces()
{
yield break;
}
public bool WillExecute
{
get { return true; }
}
}
并将所需的代码实际放入其中进行日志记录。请注意,我的示例行为没有构造函数。如果你需要在其中注入一些东西,你需要在容器中注册它(依赖项)。或者您可以自己手动创建行为并将其注册为具有统一容器的实例。
请注意,您可以使用&#34;输入&#34;变量来获取方法调用参数。您还可以使用method_return变量来获取返回值和抛出的异常(如果有)。
4)当您注册类型时,请指示unity使用我们刚刚定义的拦截行为,如下所示:
container.RegisterType<ICommandHandler<MoveCommand>, MoveHandler>(new ContainerControlledLifetimeManager(), new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<LoggingBehavior>());
container.RegisterType<ICommandHandler<CreateCommand>, CreateHandler>(new ContainerControlledLifetimeManager(), new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<LoggingBehavior>());
现在您不需要您描述的ILogger和Logger类型。
答案 1 :(得分:0)
你的Logger<T>
似乎是装饰者,但这是奇怪的构造,我不建议让它来自ICommandHandler<T>
。没有理由这样做,这使您的设计变得复杂。我建议如下:
public interface ILogger {
void Log(LogEntry entry);
}
public class FileLogger : ILogger { ... }
现在您只需将ILogger
注入需要记录的类中。如果要将记录应用于系统中的所有命令处理程序,可以为此定义装饰器:
public class LoggingCommandHandlerDecorator<T>
: ICommandHandler<T> where T : ICommand
{
private ILogger logger;
private ICommandHandler<T> decoratee;
public LoggingCommandHandlerDecorator(ILogger logger, ICommandHandler<T> decoratee)
{
this.logger = logger;
this.decoratee = decoratee;
}
public void Execute(T command)
{
// Log stuff
this.logger.Log("Executing " + typeof(T).Name + " " +
JsonConvert.SerializeObject(command));
decoratee.Execute(command);
}
}
请注意,此装饰器取决于ILogger
。它允许将日志记录应用于命令处理程序。
Stackoverflow上还有其他答案显示如何注册通用装饰器,例如this one和this one。
我建议不要使用拦截,因为使用装饰器可以使设计更清晰,更易于维护。