统一容器-包装所有解决的实现的父接口
本文关键字:解决 实现 接口 包装 | 更新日期: 2023-09-27 18:12:38
我希望能够使用统一容器,除了解决接口,包装实现一个共同的父接口的所有实例。例如,假设您为命令和处理它们的类的实现定义了公共接口:
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.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包装每一个ICommandHandler在一个ILogger时解决它。一种方法是修改每个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")));
你可以使用Unity拦截行为。您需要执行以下操作:
1)获得Unity。拦截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; }
}
}
并将实际执行日志记录所需的代码放入其中。请注意,我的示例行为没有构造函数。如果您需要在其中注入某些东西,则需要向容器注册它(依赖项)。或者你可以自己手动创建行为,并将其注册为unity容器的实例。
请注意,您可以使用"input"变量来获取方法调用参数。你也可以使用method_return变量来获取返回值和抛出的异常(如果有的话)。
当你注册你的类型时,指示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类型。
你的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上还有其他关于如何注册通用装饰器的答案,比如这个和这个。
我建议不要使用拦截,因为使用装饰器会导致更干净、更可维护的设计。