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之上有多个装饰器处理程序,因此这个解决方案会导致问题。
答案 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);