如何使用 SimpleInjector 获取特定命名空间的实例?
How can get instance of specific namespace with SimpleInjector?
public class IMessageHandler<T> {
void Handle(T message);
}
我在不同的命名空间中有多个相同类型的 IMessageHandler 实现。
Context1Namesapce
Handler1.cs
Handler2.cs
Handler3.cs
Context2Namesapce
Handler1.cs
Handler2.cs
CompositeMessageHandler.cs
解析实例时我需要属于特定命名空间的处理程序
例如,下面的代码应该return CompositeMessageHandler 仅包含 Context1Namespace 中的处理程序。
Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());
dynamic handler = _container.GetInstance(handlerType,"Context1Namespace");
如何使用 simpleinjector 实现此逻辑?
备选方案:
我可以在 GetInsance 调用后过滤已解析的实例,但我在 CompositeMessageHandler 顶部有多个装饰器处理程序,因此此解决方案会导致问题。
我认为有两种解决方案适用于您的情况。您可以手动创建 InstanceProducer
个实例并对其进行过滤以获得适当的处理程序来执行,或者您使用一些环境值并应用决定是否执行此类处理程序的装饰器。以下是两者的示例:
手动创建实例生产者:
// 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);
}
}
}
我们有一个特殊的装饰器,它依赖于简单注入器的 DecoratorContext
对象。 Simple Injector 可以将这个 class 注入到你的装饰器中,它会为你的装饰器提供更多关于它在 运行 中的上下文的信息:
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);
public class IMessageHandler<T> {
void Handle(T message);
}
我在不同的命名空间中有多个相同类型的 IMessageHandler 实现。
Context1Namesapce
Handler1.cs
Handler2.cs
Handler3.cs
Context2Namesapce
Handler1.cs
Handler2.cs
CompositeMessageHandler.cs
解析实例时我需要属于特定命名空间的处理程序
例如,下面的代码应该return CompositeMessageHandler 仅包含 Context1Namespace 中的处理程序。
Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());
dynamic handler = _container.GetInstance(handlerType,"Context1Namespace");
如何使用 simpleinjector 实现此逻辑?
备选方案: 我可以在 GetInsance 调用后过滤已解析的实例,但我在 CompositeMessageHandler 顶部有多个装饰器处理程序,因此此解决方案会导致问题。
我认为有两种解决方案适用于您的情况。您可以手动创建 InstanceProducer
个实例并对其进行过滤以获得适当的处理程序来执行,或者您使用一些环境值并应用决定是否执行此类处理程序的装饰器。以下是两者的示例:
手动创建实例生产者:
// 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);
}
}
}
我们有一个特殊的装饰器,它依赖于简单注入器的 DecoratorContext
对象。 Simple Injector 可以将这个 class 注入到你的装饰器中,它会为你的装饰器提供更多关于它在 运行 中的上下文的信息:
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);