[英]Autofac: Resolving generic interface in a generic method
通用接口:
public interface IGeneric<T>{}
客户端:
public class ClientClass
{
public void DoSomething<T>()
{
//what to inject in constructor
//to get an implementation of the IGeneric<T> from autofac?
}
}
知道如何做到这一点吗?
假设你有一个实现你的接口的类
public class MyGeneric<T> : IGeneric<T>
{
}
并且您已将其注册到您的容器中
builder.RegisterGeneric(typeof(MyGeneric<>)).As(typeof(IGeneric<>));
然后你可以在你的DoSomething
方法中像这样解决它
public class ClientClass
{
private readonly ILifetimeScope _scope;
public ClientClass(ILifetimeScope scope)
{
_scope = scope;
}
public void DoSomething<T>()
{
var myGeneric = _scope.Resolve<IGeneric<T>>();
}
}
如您所见,您需要在DoSomething
方法中使用 Autofac 范围 ( ILifetimeScope
) 的实例。 您可以使用构造函数注入它。 据我所知,没有其他方法,因为您的ClientClass
本身不是通用的。 您不能使用构造函数或属性注入来获取IGeneric<T>
实例,因为您在创建ClientClass
实例时不知道T
的类型。
在我看来,您有两个选择:
ClientClass
并使用它来解析IGeneric<T>
实例(如上所示)ClientClass
通用并在构造函数中注入IGeneric<T>
实例我遇到了同样的问题,我想出的解决方案涉及方法拦截。 考虑以下类:
public class InjectionInterceptor : IInterceptor {
private readonly ILifetimeScope _Scope;
public InjectionInterceptor(ILifetimeScope scope) {
_Scope = scope;
}
public void Intercept(IInvocation invocation) {
using (var lifetime = _Scope.BeginLifetimeScope(string.Format("InjectionInterceptor {0}", Guid.NewGuid()))) {
InjectDependencyIfNecessary(invocation, lifetime);
invocation.Proceed();
}
}
private void InjectDependencyIfNecessary(IInvocation invocation, ILifetimeScope lifetime) {
int indexOfDependencyArgument = FindDependencyArgument(invocation.Method);
if (indexOfDependencyArgument >= 0 && invocation.GetArgumentValue(indexOfDependencyArgument) == null) {
SetDependencyArgument(invocation, indexOfDependencyArgument, lifetime);
}
}
private static int FindDependencyArgument(System.Reflection.MethodInfo method) {
var allArgs = method.GetParameters();
return Array.FindIndex(allArgs, param =>
param.ParameterType.IsInterface &&
param.ParameterType.IsGenericType &&
param.ParameterType.GetGenericTypeDefinition() == typeof(IGeneric<>));
}
private void SetDependencyArgument(IInvocation invocation, int indexOfDependencyArgument, ILifetimeScope lifetime) {
var methodArg = invocation.Method.GetGenericArguments().Single();
var dependency = lifetime.Resolve(typeof(IGeneric<>).MakeGenericType(methodArg));
invocation.SetArgumentValue(indexOfDependencyArgument, dependency);
}
}
注册您的客户端类以被此类拦截:
var builder = new ContainerBuilder();
builder.RegisterType<InjectionInterceptor>();
builder.RegisterType<ClientClass>()
.EnableClassInterceptors()
.InterceptedBy(typeof(InjectionInterceptor));
更改您的方法以接受您的 IGeneric 实例:
public class ClientClass
{
public virtual void DoSomething<T>(IGeneric<T> dependency = null) //must be virtual to be intercepted
{
if (dependency == null) throw new ArgumentNullException(nameof(dependency));
//use dependency here
}
}
假设您的 ClientClass 由 Autofac 解析,每个方法(标记为虚拟的)都将被此类拦截。 它将检查方法参数并尝试找到一个 IGeneric。 如果传入的参数为空,则它将检查被调用方法的泛型类型参数,并解析 IGeneric 的实例。 然后它将参数设置为该解析值。
您不必使依赖项成为默认参数,但它允许您像往常一样调用您的方法,同时仍然可以根据需要选择注入特定类型:
client.DoSomething<int>(); //injected by the interceptor
client.DoSomething(new Generic<int>()); // resolved manually; interceptor does nothing
此方法的一个大缺点是,如果您只使用空参数列表调用 DoSomething() 方法,则可能很难被其他可能正在处理代码和/或调试代码的人理解。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.