Unity Container - 包装父接口的所有解析实现
Unity Container - wrap all resolved implementations of parent interface
除了解析接口之外,我希望能够使用 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.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 调用。但是,本着 "Don't Repeat Yourself" 的精神,我真的很想能够指定一次 all ICommandHandler 类型应该另外包装在适当类型的 ILogger 中。可能有大量的 ICommandHandler 类型被注册,以及用于错误处理、身份验证等的额外包装器,因此重复和疏忽的机会会很大。有没有办法一次将包装器应用于所有这些?
编辑:这是我正在寻找的语法,改编自已接受的答案和已接受答案中的第一个 link:
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.Interception NuGet 包。
2) 指示统一容器使用这样的拦截扩展:
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; }
}
}
并将实际进行日志记录所需的代码放入其中。请注意,我的示例行为没有构造函数。如果您需要在其中注入一些东西,您需要在容器中注册它(依赖项)。或者您可以自己手动创建行为并将其注册为统一容器的实例。
请注意,您可以使用 "input" 变量来获取方法调用参数。您还可以使用 method_return 变量来获取 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 类型。
你的 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
。它允许将日志记录应用于命令处理程序。
Whosebug 上还有其他答案展示了如何注册通用装饰器,例如 this one and this one。
我建议不要使用拦截,因为使用装饰器会使设计更简洁、更易于维护。
除了解析接口之外,我希望能够使用 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.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 调用。但是,本着 "Don't Repeat Yourself" 的精神,我真的很想能够指定一次 all ICommandHandler 类型应该另外包装在适当类型的 ILogger 中。可能有大量的 ICommandHandler 类型被注册,以及用于错误处理、身份验证等的额外包装器,因此重复和疏忽的机会会很大。有没有办法一次将包装器应用于所有这些?
编辑:这是我正在寻找的语法,改编自已接受的答案和已接受答案中的第一个 link:
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.Interception NuGet 包。
2) 指示统一容器使用这样的拦截扩展:
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; }
}
}
并将实际进行日志记录所需的代码放入其中。请注意,我的示例行为没有构造函数。如果您需要在其中注入一些东西,您需要在容器中注册它(依赖项)。或者您可以自己手动创建行为并将其注册为统一容器的实例。
请注意,您可以使用 "input" 变量来获取方法调用参数。您还可以使用 method_return 变量来获取 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 类型。
你的 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
。它允许将日志记录应用于命令处理程序。
Whosebug 上还有其他答案展示了如何注册通用装饰器,例如 this one and this one。
我建议不要使用拦截,因为使用装饰器会使设计更简洁、更易于维护。