Unity Container - 包装所有已解析的父接口实现

时间:2015-09-13 22:14:25

标签: c# .net dependency-injection unity-container

我希望能够使用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")));

2 个答案:

答案 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 onethis one

我建议不要使用拦截,因为使用装饰器可以使设计更清晰,更易于维护。