2016-06-09 55 views
0

我有一個Web API項目,我試圖在運行時解析查詢處理程序,並給出查詢類型和查詢結果類型。以下是我用作概念驗證的一些測試代碼。我正在使用Ninject Factory擴展,但是我無法獲得正確的設置,因爲我想返回一個通用接口,也許這是錯誤的設計,所以任何建議都會很感激。嘗試使用Ninject Factory擴展解決依賴性時出錯

代碼

public interface IQueryHandlerFactory 
{ 
    IQueryHandler<IQuery, IQueryResult> Resolve(Type type); 
} 

public interface IQuery 
{ 
} 

public interface IQueryResult 
{ 
} 

public interface IQueryHandler<TQuery, TResult> 
    where TQuery : IQuery 
    where TResult : IQueryResult 
{ 
    TResult Execute(TQuery query); 
} 

public class PaymentQueryHandler : IQueryHandler<GetPayments, PaymentResult> 
{ 
    public PaymentResult Execute(GetPayments query) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class UserQueryHandler : IQueryHandler<GetUsers, UserResult> 
{ 
    public UserResult Execute(GetUsers query) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class TypeInstanceProvider : StandardInstanceProvider 
{ 
    protected override Type GetType(MethodInfo methodInfo, object[] arguments) 
    { 
     var type = arguments[0] as Type; 

     return type; 
    } 

    protected override IConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments) 
    { 
     return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray(); 
    } 

    public override object GetInstance(IInstanceResolver instanceResolver, MethodInfo methodInfo, object[] arguments) 
    { 
     Type t = this.GetType(methodInfo, arguments); 

     var test = instanceResolver.Get(t, 
           null, 
           null, 
           this.GetConstructorArguments(methodInfo, arguments), 
           false); 

     return test; 
    } 
} 

我只是用一些Ninject測試調用我NinjectWebCommon現在,看看它是否會工作。我想能夠使用MakeGenericType方法,因爲我知道查詢類型和結果類型。它的工作原理,當我打電話var test = kernel.Get(queryHandlerType);然而,當我嘗試使用下面

NinjectWebCommon代碼工廠失敗:

kernel.Bind<IQueryHandlerFactory>().ToFactory(() => new TypeInstanceProvider()); 

var queryHandlerType = typeof(IQueryHandler<,>).MakeGenericType(typeof(GetPayments), typeof(PaymentResult)); 
var factory = kernel.Get<IQueryHandlerFactory>(); 

var test = kernel.Get(queryHandlerType); // This works 
var handler = factory.Resolve(queryHandlerType); // This fails 

所產生的誤差小於

Exception thrown: 'System.InvalidCastException' in DynamicProxyGenAssembly2 

Additional information: Unable to cast object of type 'PaymentQueryHandler' to type 'IQueryHandler`2[IQuery,IQueryResult]'. 

我很可能超過使事情變得複雜,或者這甚至有可能?我會這樣的。有沒有人有任何建議,讓這個工作?

回答

0

在我看來,你確實是過於複雜的事情。工廠有什麼意義?如果kernel.Get(queryHandlerType)有效,爲什麼不使用它?

怎麼樣這個非常簡單的實現:

public interface IQueryHandlerFactory 
{ 
    IQueryHandler<TQuery, TQueryResult>() 
     where TQuery : IQuery, TQueryResult : IQueryResult; 
} 

public class QueryHandlerFactory : IQueryHandlerFactory 
{ 
    private readonly IResolutionRoot resolutionRoot; 
    public QueryHandlerFactory(IResolutionRoot resolutionRoot) 
    { 
     this.resolutionRoot = resolutionRoot; 
    } 

    IQueryHandler<TQuery, TQueryResult>() 
     where TQuery : IQuery, TQueryResult : IQueryResult 
    { 
     var queryHandlerType = typeof(IQueryHandler<TQuery,TQueryResult>); 
     return (IQueryHandler<TQuery, TQueryResult>)kernel.Get(queryHandlerType); 
    } 
} 
+0

您的解決方案將我指向正確的方向,謝謝 – Gavin

0

我會說它根本不起作用。

問題是,動態創建的工廠使用provider.GetInstance方法返回您的對象。然後它會嘗試將其投射到目標界面。

這意味着,在你的榜樣它下面的內容:

var obj = // your PaymentQueryHandler object 
(IQueryHandler<IQuery, IQueryResult>)obj 

但它不會工作,因爲你的接口不通過應用out modifier使covariance

但即使在這裏,你有一個問題。你的接口使用第一個泛型參數,一個輸入類型和第二個泛型參數作爲輸出參數。這意味着您需要啓用contravariance。但在這種情況下,您將無法轉換您的類型,因爲您需要將其轉換爲更多派生類型。

這就是爲什麼它不適用於你的工廠。

它可以與標準get調用一起使用,因爲它知道目標類型並且不使用鑄造到IQueryHandler<IQuery, IQueryResult>

+0

因此,即使不應用協方差等我仍然有一個問題。我可以修改我的解決方案以滿足我的需求,所以你建議使用提供商等替代方法,或者你看到同樣的問題產生?如果我注入內核(我知道,我知道)它至少能起作用嗎? – Gavin

+0

是的,嘗試使用提供者。 – MaKCbIMKo

+0

我唯一關心的提供者是我再次遇到了投射問題。從來沒有使用過它們,你有一個例子,我可以通過泛型類型(從MakeGenericType調用)作爲參數? – Gavin