繁体   English   中英

Autofac:在泛型方法中解析泛型接口

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM