未注册类型的 SimpleInjector force Lifestyle.Transient

SimpleInjector force Lifestyle.Transient for Unregistered-Type

我有一个包装器 class 如下:

// this is not used for IoC purposes, but by code to cast object to known T
internal interface ITaskCompletionSourceWrapper
{
    void SetResult(object result);
    /* snip */
}

internal class TaskCompletionSourceGenericWrapper<TResult> : ITaskCompletionSourceWrapper
{
    readonly TaskCompletionSource<TResult> tcs;
    public TaskCompletionSourceGenericWrapper() =>        
        this.tcs = new TaskCompletionSource<TResult>(TaskCreationOptions.RunContinuationsAsynchronously);
    public void SetResult(object result) => tcs.SetResult((TResult)result);
    /* snip */
}

我目前使用 Activator 实例化的地方:

var responseType = request
                     .GetType()
                     .GetInterfaces()
                     .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>))
                     GetGenericArguments()[0];

var wrapperType = typeof(TaskCompletionSourceGenericWrapper<>).MakeGenericType(responseType);
var tcsWrapper = this.container.GetInstance(wrapperType) as ITaskCompletionSourceWrapper;

我将其切换到 SimpleInjector(因为可能会使用日志记录和其他 IOC 优点)并认为一切都很好,直到我发现一个问题(这是由于共享的生活方式):

/* snip responseType code from above */
var wrapperType = typeof(TaskCompletionSourceGenericWrapper<>).MakeGenericType(responseType);
var tcsWrapper = this.container.GetInstance(wrapperType) as ITaskCompletionSourceWrapper;

// i can see the type has been registered as scoped, but we need it to be unique
[30] = ServiceType = TaskCompletionSourceGenericWrapper<Object>, Lifestyle = Async Scoped

上面的代码在 AsyncScopedLifestyle.BeginScope 块中被调用。

我想在运行时使用 SimpleInjector 实例化此包装器(但确保 Lifestyle.Transient):

包装器的类型在 intstringbool 等中几乎是任意的,但也可以是简单的 POCO classes.

这类似于开放通用注册的情况,但我宁愿避免让其他开发人员为简单类型指定程序集(因为他们肯定会忘记,而且他们的 classes 无论如何都很简单 - command/query POCO 等)。

请问我怎样才能达到上面的效果,还是我必须注册集合?

或者这是否与生活方式不匹配更深——我希望不会,因为包装器没有依赖项(也许除了 ILogger 之外不会)只是在做一些简单的实例化 class(我想替换 Activator 而不是解耦组件 IoC 样式)。

根据他们的文档Batch-Registration / Auto-Registration和经验,使用依赖注入时需要注册接口的实现

var container = new Container();
// If related classes exist in a different assembly then add them here
var assemblies = new[] { typeof(TaskCompletionSourceGenericWrapper<>).Assembly };

container.Collection.Register(typeof(TaskCompletionSourceGenericWrapper<>), assemblies, Lifestyle.Transient);

DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

我能想到但尚未测试的一个 hack 是创建一个包装器 class,returns 一个包装器的深层副本 a.k.a 克隆。返回一个对象的克隆类似于 transient,因为它每次请求它时 returns 一个新实例,但这种方法的缺点是你只能为可序列化的 classes 这样做,所以你可能需要添加 [Serializable()]。有关详细信息,请参阅 Deep cloning objects

public interface IUglyDuckling
{
    ITaskCompletionSourceWrapper GetITaskCompletionSourceWrapper(Type type);
}
public class UglyDuckling : IUglyDuckling
{
    public ITaskCompletionSourceWrapper GetITaskCompletionSourceWrapper(Type type)
    {
        var wrapperType = typeof(TaskCompletionSourceGenericWrapper<>).MakeGenericType(type);
        var tcsWrapper = this.container.GetInstance(wrapperType) as ITaskCompletionSourceWrapper;
        return ObjectCopier.Clone<ITaskCompletionSourceWrapper>(tcsWrapper);
    }
}