5

Wir waren mit einer eigenständigen Geschichte beginnen werde, nur damit Sie verstehen, warum: ich irgendwelche Aktionen behandeln wollen, die Daten gegen die gleiche Schnittstelle zu ändern: ICommand Es sind die Dinge, die da draußen genannt ICommandHandlers existieren das handle jeden Befehl, den ich will. Also, wenn ich einen CreatePersonCommand brauche, würde ich einen CreatePersonCommandHandler da draußen brauchen.RegisterOpenGeneric mit SimpleInjector löst falschen Typ

Also hier ist der Körper einer Konsolenanwendung, die dies veranschaulicht: (Erfordert Simple Injector)

// The e.g. CreatePersonCommand, with TResult being Person, as an example. 
public interface ICommand<TResult> 
{ 
} 

//This handles the command, so CreatePersonCommandHandler 
public interface ICommandHandler<in TCommand, out TResult> 
    where TCommand : ICommand<TResult> 
{ 
    TResult Handle(TCommand command); 
} 

// Imagine a generic CRUD set of operations here where we pass 
// in an instance of what we need made 
public class CreateBaseCommand<TModel> : ICommand<TModel> 
{ 
    public TModel ItemToCreate { get; set; } 
} 

public class DeleteBaseCommand<TModel> : ICommand<TModel> 
{ 
    public TModel ItemToDelete { get; set; } 
} 

public class CreateCommandBaseHandler<TModel> 
    : ICommandHandler<CreateBaseCommand<TModel>, TModel> 
{ 
    public TModel Handle(CreateBaseCommand<TModel> command) 
    { 
     // create the thing 
     return default (TModel); 
    } 
} 

public class DeleteCommandBaseHandler<TModel> 
    : ICommandHandler<DeleteBaseCommand<TModel>, TModel> 
{ 
    public TModel Handle(DeleteBaseCommand<TModel> command) 
    { 
     // delete the thing 
     return default(TModel); 
    } 
} 

public class Program 
{ 
    private static Container container; 

    static void Main(string[] args) 
    { 
     container = new Container(); 

     // Order does not seem to matter, I've tried both ways. 
     container.RegisterOpenGeneric(typeof(ICommandHandler<,>), 
      typeof(DeleteCommandBaseHandler<>)); 
     container.RegisterOpenGeneric(typeof(ICommandHandler<,>), 
      typeof(CreateCommandBaseHandler<>)); 

     container.Verify(); 

     // So I want to make the usual hello world 
     var commandToProcess = new CreateBaseCommand<string> { ItemToCreate = "hello world"}; 

     // Send it away! 
     Send(commandToProcess); 
    } 

    private static void Send<TResult>(ICommand<TResult> commandToProcess) 
    { 
     //{CreateBaseCommand`1[[System.String,..."} 
     var command = commandToProcess.GetType(); 
     //{Name = "String" FullName = "System.String"} 
     var resultType = typeof (TResult); 

     //"ICommandHandler`2[[CreateBaseCommand`1[[System.String,..."} 
     // so it's the right type here 
     var type = typeof(ICommandHandler<,>).MakeGenericType(command, resultType); 

     // This is where we break! 
     var instance = container.GetInstance(type); 
     // The supplied type DeleteCommandBaseHandler<String> does not implement 
     // ICommandHandler<CreateBaseCommand<String>, String>. 
     // Parameter name: implementationType 
    } 
} 

Also aus irgendeinem Grund SimpleInjector versucht immer, die DeleteCommandHandler<> für die CreateBaseCommand<> zu lösen, die ich habe. Auch hier spielt die Reihenfolge keine Rolle. Ich habe andere, closed-type, commandhandlers (und ihre jeweiligen Befehle), die nur ICommandHandler<,> erben, die gut funktionieren.

Ich verbrachte eine ganze Weile damit, alle möglichen Arten der Registrierung zu durchlaufen, die ich von this konnte.

+0

Der Fehler, den Sie angetroffen haben, ist wirklich ärgerlich. Ich habe gerade das gleiche Problem beim Schreiben eines Dekorators in einer meiner eigenen Anwendungen bekommen. Ich überlege, eine Patch-Version (2.3.6) zu veröffentlichen, um diesen Fehler zu beheben, anstatt auf den nächsten Minor (2.4) zu warten, da es in einigen Szenarien wirklich schwierig ist, diesen Fehler zu umgehen. – Steven

+0

Das sind gute Nachrichten, ich habe mich nicht auf andere Fälle gefreut. –

Antwort

4

UPDATE:

Dies ist definitiv ein Fehler in der aktuellen Version. Dies rutschte irgendwie durch die Einheit, die Risse prüfte. Der Code vermisst eine Überprüfung, die überprüft, ob eine erstellte geschlossene generische Implementierung tatsächlich den angeforderten geschlossenen generischen Diensttyp implementiert. Wenn alle Einschränkungen des generischen Typs gültig sind, betrachtet das Framework die Auflösung als erfolgreich, was in Ihrem Fall nicht korrekt ist.

Die Reparatur ist ziemlich einfach und die kommende Version 2.4 wird das definitiv beheben, aber in der Zwischenzeit müssen Sie mit der folgenden Problemumgehung arbeiten.

UPDATE 2:

Die eigentlich ganz böse und kann sehr schwierig sein, in einigen Fällen zu umgehen. Neben RegisterOpenGeneric sind auch Dekorierer-Registrierungen betroffen. Dies führte mich zu dem Schluss, dass dies schnell behoben werden musste und nicht bis zur nächsten kleinen Veröffentlichung warten kann. Ich habe daher Version 2.3.6 auf NuGet und CodePlex geschoben. v2.3.6 behebt dieses Problem.

Abhilfe:

dieses Problem umgehen, liefert generische Typargumente zu verhindern, die in andere Typen (wie Ihr DeleteBaseCommand<TModel>) verschachtelt sind. Stattdessen können Sie stattdessen auf generische Typeinschränkungen zurückgreifen, wie im folgenden Beispiel zu sehen ist:

public class CreateCommandBaseHandler<TCommand, TModel> 
    : ICommandHandler<TCommand, TModel> // no nested generic arguments here 
    where TCommand : CreateBaseCommand<TModel> // but type constraint here. 
{ 
    public TModel Handle(TCommand command) 
    { 
     // create the thing 
     return default(TModel); 
    } 
} 

public class DeleteCommandBaseHandler<TCommand, TModel> 
    : ICommandHandler<TCommand, TModel> 
    where TCommand : DeleteBaseCommand<TModel> 
{ 
    public TModel Handle(TCommand command) 
    { 
     // delete the thing 
     return default(TModel); 
    } 
} 
Verwandte Themen