簡體   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