如何使用SimpleInjector获取特定命名空间的实例?

时间:2016-06-04 20:22:51

标签: c# dependency-injection simple-injector

public class IMessageHandler<T> { 
    void Handle(T message);
}

我在不同的命名空间中有多个相同类型的IMessageHandler实现。

Context1Namesapce
  Handler1.cs
  Handler2.cs
  Handler3.cs
Context2Namesapce
  Handler1.cs
  Handler2.cs
CompositeMessageHandler.cs

在解析实例

时,我需要属于特定命名空间的处理程序

例如,下面的代码应该返回CompositeMessageHandler只包含Context1Namespace中的Handlers。

 Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());
 dynamic handler = _container.GetInstance(handlerType,"Context1Namespace");

如何使用simpleinjector实现此逻辑?

替代解决方案: 我可以在GetInsance调用之后过滤已解析的实例,但我在CompositeMessageHandler之上有多个装饰器处理程序,因此这个解决方案会导致问题。

1 个答案:

答案 0 :(得分:0)

我能想到的两个解决方案可以在你的场景中使用。您可以手动创建InstanceProducer实例并对其进行过滤以获取正确的处理程序,或者您使用某些环境值并应用装饰器来决定是否执行此类处理程序。以下是两者的示例:

手动创建InstanceProducers:

// During registration
var handlerProducerInfos = (
    from type in container.GetTypesToRegister(typeof(IMessageHandler<>), assemblies)
    let registration = Lifestyle.Transient.CreateRegistration(type, container)
    from service in type.GetClosedInterfacesFor(typeof(IMessageHandler<>))
    let producer = new InstanceProducer(service, registration)
    select new
    {
        Producer = new InstanceProducer(service, registration),
        Namespace = type.Namespace
    })
    .ToArray();

// During execution
Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());

var handlers =
    from info in handlerProducerInfos
    where handlerType == info.Producer.ServiceType
    where info.Namespace == "Context1Namespace"
    select info.Producer.GetInstance();

foreach (dynamic handler in handlers) {

}

使用环境值和特殊装饰器:

我们可以像往常一样使用CompositeMessageHandler<T>

public class CompositeMessageHandler<T> : IMessageHandler<T> {
    private readonly IEnumerable<IMessageHandler<T>> handlers;
    public CompositeMessageHandler(IEnumerable<IMessageHandler<T>> handlers) {
        this.handlers = handlers;
    }

    public void Handle(T message) {
        foreach (var handler in this.handlers) {
            handler.Handle(message);
        }
    }
}

我们有一个特殊的装饰器,它依赖于Simple Injector的DecoratorContext对象。 Simple Injector可以将这个类注入到装饰器中,它会为装饰器提供有关其运行的上下文的更多信息:

public class ContextualMessageHandlerDecorator<T> : IMessageHandler<T> {
    private readonly DecoratorContext context;
    private readonly IMessageHandler<T> decoratee;

    public ContextualMessageHandlerDecorator(DecoratorContext context,
        IMessageHandler<T> decoratee) {
        this.context = context;
        this.decoratee = decoratee;
    }

    public static string ContextNamespace { get; set; }

    public void Handle(T message) {
        // Here we get the ambient value from the ContextHelper and match it
        // with the namespace of the real message handler
        if (ContextHelper.ContextNamespace.Value.Equals(
            this.context.ImplementationType.Namespace)) {
            this.decoratee.Handle(message);
        }
    }
}

public static ContextHelper {
    public static readonly ThreadLocal<string> ContextNamespace =new ThreadLocal<string>();
}

我们可以按如下方式注册所有内容:

// Register the concrete message handlers as collection
container.RegisterCollection(typeof(IMessageHandler<>), assemblies);

// Register the composite to wrap the real message handlers
container.Register(typeof(IMessageHandler<>), typeof(CompositeMessageHandler<>));

// Register your decorators here:

// Register our 'contextual' decorator last, but prevent it to be wrapped 
// around the CompositeMessageHandler.
container.RegisterDecorator(typeof(IMessageHandler<>), 
    typeof(ContextualMessageHandlerDecorator<>),
    c => !c.ImplementationType.Name.StartsWith("CompositeMessageHandler"));

执行期间:

Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());
// Resolves the composite handler, wrapping all handlers for T.
dynamic handler = _container.GetInstance(handlerType);
// Set the context to allow filtering on namespace
ContextHelper.ContextNamespace.Value = "Context1Namespace";
// handle the message
handler.Handle((dynamic)message);