简单注入器:注入IEnumerable<Func<T>>

3
我如何使用 Simple Injector 注入 IEnumerable>?
简要介绍一下,我正试图创建所有 EventHandlers ,这些处理程序知道如何处理一个特定的事件。这是我的容器注册代码:
container.RegisterCollection(typeof(IHandleDomainEvent<>),
    AppDomain.CurrentDomain.GetAssemblies());

以下是两个实现相同事件的 IHandleEvent<T> 接口的类:

public class Reservation : IHandleDomainEvent<OrderConfirmed>{}
public class Order: IHandleDomainEvent<OrderConfirmed>{}

所以当我调用Simple Injector时:
var handlers = _container.GetAllInstances<Func<IHandleDomainEvent<OrderConfirmed>>>();

我希望能够收到 IEnumerable<Func<IHandleDomainEvent<OrderConfirmed>>>

只是为了澄清,我知道如果我调用:

var handlers = _container.GetAllInstances<IHandleDomainEvent<OrderConfirmed>>();

我会得到一个 `IEnumerable>`。
对于只有一个实现的接口,可以使用以下方式进行注册:
container.Register(typeof(IHandleDomainEvent<>),
    AppDomain.CurrentDomain.GetAssemblies(), Lifestyle.Scoped);

在注册的末尾添加以下ResolveUnregisteredType:
container.ResolveUnregisteredType += (o, args) => ResolveFuncOfT(o, args, container);

// Function
private static void ResolveFuncOfT(object s, UnregisteredTypeEventArgs e, Container container)
{
    var type = e.UnregisteredServiceType;
    if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Func<>)) return;
    Type serviceType = type.GetGenericArguments().First();
    InstanceProducer producer = container.GetRegistration(serviceType, true);
    Type funcType = typeof(Func<>).MakeGenericType(serviceType);
    var factoryDelegate = Expression.Lambda(funcType, producer.BuildExpression()).Compile();
    e.Register(Expression.Constant(factoryDelegate));
}

将允许调用:

var handler = _container.GetInstance<Func<IHandleDomainEvent<TEvent>>>();
1个回答

4
这就是Simple Injector的优美之处:您永远不必解析IEnumerable<Func<T>>,因为由Simple Injector解析的任何IEnumerable<T>都已经作为一个运行。
这意味着当您解析IEnumerable<T>时,中的元素都不会被解析。只有在枚举流时才会逐个解析。
在枚举流时,元素将根据其生命周期进行解析。这意味着当集合中的元素是Transient时,两次枚举流将导致创建新的瞬态实例。
例如:
// Only resolves the enumerable, not the contained handlers.
// This enumerable itself is a singleton, you can reference it forever.
var collection = container.GetInstances<IEventHandler<OrderConfirmed>>();

// Calls back into the container to get the first element, but nothing more
var first = collection.First();

// Since the stream that Simple Injector returns is a IList<T>, getting the last
// element is an O(1) operation, meaning that only the last element is resolved;
// not the complete collection.
var last = collection.Last();

// Calling .ToArray(), however, will obviously resolve all registrations that are 
// part of the collection.
var all = collection.ToArray();

// Iterating a stream will always call back into the container, which ensures
// that the stream adheres to the elements lifestyles. Transients will be
// created on each iteration, while singletons will only be created once.
container.Register<Apple>(Lifestyle.Transient);
container.Register<Banana>(Lifestyle.Singleton);
container.RegisterCollection<IFruit>(typeof(Apple), typeof(Banana));
var fruits = container.GetAllInstances<IFruit>();
Assert.AreNotSame(fruits.First(), fruits.First());
Assert.AreSame(fruits.Last(), fruits.Last());

// Even other collection types such as IReadOnlyCollection<T> behave as streams
var col = container.GetInstance<IReadOnlyCollection<IEventHandler<OrderConfirmed>>();

// This gives you the possibility to get a particular item by its index.
var indexedItem = col[3];

您可以在 Simple Injector 的这里找到有关处理集合的更多信息:


不错!但我不确定它是否会对我有所帮助。因为我需要simpleinjector每次调用Func<T>时都返回一个实例。所以,如果我在你的示例中两次调用collection.First(),那么两个实例将是相同的。 最后,我唯一看到的出路就是创建一个打开新作用域并在其中实例化类T的类。因此,每次我调用它时都在新作用域内实例化。 - Rodrigo Riskalla Leal
如果您每次调用“first”时需要一个新的实例,请确保将您的处理程序注册为瞬态。您似乎当前已将它们注册为Scoped或Singleton。 - Steven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接