2016-06-09 3 views
0

Ich habe ein Web-API-Projekt, in dem ich versuche, einen Abfrage-Handler zur Laufzeit mit dem Abfragetyp und dem Abfrage-Ergebnistyp aufzulösen. Unten ist ein Testcode, den ich als Beweis für das Konzept verwende. Ich benutze die Ninject Factory-Erweiterung, aber ich habe Probleme, die richtige Einstellung zu bekommen, da ich eine generische Schnittstelle zurückgeben möchte, vielleicht ist dies das falsche Design, so dass alle Vorschläge dankbar wären.Fehler beim Lösen der Abhängigkeit mit Ninject Factory-Erweiterungen

-Code

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; 
    } 
} 

Ich bin gerade mit einigen Ninject Prüfung ruft in meinem NinjectWebCommon für jetzt, um zu sehen, ob es funktionieren würde. Ich möchte in der Lage sein, die MakeGenericType Methode zu verwenden, da ich den Abfragetyp und den Ergebnistyp kenne. Es funktioniert, wenn ich var test = kernel.Get(queryHandlerType); jedoch nennen versagt es, wenn ich versuche unter

NinjectWebCommon Code, um die Fabrik zu verwenden:

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 

Der Fehler, der erzeugt wird, ist unter

Exception thrown: 'System.InvalidCastException' in DynamicProxyGenAssembly2 

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

kann ich gut über sein Dinge komplizieren, oder ist das überhaupt möglich? Ich würde es haben. Hat jemand irgendwelche Vorschläge, um das zu funktionieren?

Antwort

0

Es scheint mir, dass Sie die Dinge in der Tat übermäßig komplizieren. Was ist der Sinn der Fabrik? Wenn kernel.Get(queryHandlerType) funktioniert, warum benutzt du es nicht?

Wie über diese sehr einfache Implementierung:

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

Ihre Lösung wies mich in die richtige Richtung, danke – Gavin

0

Ich würde sagen, es wird überhaupt nicht funktionieren.

Das Problem besteht darin, dass die dynamisch erstellte Factory provider.GetInstance Methode verwendet, um Ihr Objekt zurückzugeben. Und dann versucht es, es auf die Zieloberfläche zu übertragen.

Es bedeutet, dass es etwas tut, das folgende in Ihrem Beispiel:

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

Aber es wird nicht funktionieren, weil Sie Ihre Schnittstelle durch die Anwendung out modifier nicht covariance ermöglichen.

Aber auch hier hast du ein Problem. Ihre Schnittstelle verwendet das erste allgemeine Argument und einen Eingabetyp und ein zweites generisches Argument als Ausgabeargument. Es bedeutet, dass Sie contravariance aktivieren müssen. In diesem Fall können Sie Ihren Typ jedoch nicht konvertieren, da Sie ihn in einen abgeleiteten Typ konvertieren müssen.

Deshalb funktioniert es nicht mit Ihnen Fabrik.

Und es funktioniert mit dem Standard get Aufruf, weil es einen Zieltyp kennt und Casting zu IQueryHandler<IQuery, IQueryResult> nicht verwendet.

+0

So kann auch ohne Anwendung von Kovarianz usw. Ich habe immer noch ein Problem. Ich kann meine Lösung an meine Bedürfnisse anpassen, also empfehlen Sie einen alternativen Ansatz mit Anbietern usw. oder sehen Sie das gleiche Problem? Wenn ich den Kernel injizieren würde (ich weiß, ich weiß) würde es zumindest funktionieren? – Gavin

+0

Ja, versuchen Sie stattdessen, Provider zu verwenden. – MaKCbIMKo

+0

Meine einzige Sorge mit Anbietern ist, dass ich wieder in das Casting-Problem stoße. Haben Sie ein Beispiel, in dem ich den generischen Typ (aus dem MakeGenericType-Aufruf) als Parameter übergeben kann? – Gavin

Verwandte Themen