未注册类型的 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
):
包装器的类型在 int
、string
、bool
等中几乎是任意的,但也可以是简单的 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);
}
}
我有一个包装器 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
):
包装器的类型在 int
、string
、bool
等中几乎是任意的,但也可以是简单的 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);
}
}