统一容器-包装所有解决的实现的父接口

本文关键字:解决 实现 接口 包装 | 更新日期: 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上还有其他关于如何注册通用装饰器的答案,比如这个和这个。

我建议不要使用拦截,因为使用装饰器会导致更干净、更可维护的设计。