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