5

'u çözer Bağımsız bir hikaye ile başlayacağız, sadece neden olduğunu anlıyoruz: Aynı arabirime göre verileri değiştiren tüm eylemleri işlemek istiyorum: ICommand Orada ICommandHandlers denilen şeyler var İstediğim herhangi bir komutu hallediyorum. Yani, bir CreatePersonCommand istiyorum, orada bir CreatePersonCommandHandler gerekir. SimpleInjector hep var CreateBaseCommand<> için DeleteCommandHandler<> çözümlemeye çalışır hangi sebeple olursa olsun YaniSimpleInjector ile RegisterOpenGeneric yanlış tip

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

(Simple Injector gerektirir):

Yani burada bu gösteren bir konsol uygulaması vücudu. Yine, sipariş önemli değil. Sadece düzgün çalışan ICommandHandler<,>'u devralan başka, kapalı tip, komut dosyalarına (ve onların kendi komutlarına) sahibim.

this'dan alabildiğim her olası kayıt türünden geçerek biraz zaman geçirdim.

+0

Karşılaştığınız hata gerçekten can sıkıcı. Kendi uygulamalarımızdan birinde bir dekoratör yazarken aynı soruna girdim. Bu hatayı düzeltmek için bir yama sürümü (2.3.6) hazırlamayı düşünüyorum, çünkü bazı senaryolarda bu hatanın etrafından çalışmak gerçekten zor. – Steven

+0

Bu iyi haber, başka davaları da kendim beklemiyordum. –

cevap

4

GÜNCELLEME:

Bu kesinlikle geçerli sürümde bir hata değildir. Bu bir şekilde ünite test çatlaklarından geçti. Kod, kapalı bir genel uygulama gerçekte istenen kapalı genel hizmet türünü gerçekten uygulayıp uygulamadığını doğrulayan bir denetimi özler. Tüm jenerik tip kısıtlamaları geçerliyse, çerçeve sizin kararınızda yanlış olan çözünürlüğü başarılı olarak değerlendirir.

Düzeltme işlemi oldukça kolay ve gelen v2.4 bunu kesinlikle düzeltir, ancak bu arada aşağıdaki geçici çözümle de karşılaşırsınız.

GÜNCELLEME 2:

aslında oldukça kötü olduğunu ve bazı durumlarda çözüm için oldukça zor olabilir. RegisterOpenGeneric'un yanı sıra, dekoratör kayıtları da etkilenir. Bu, bunun hızlı bir şekilde düzeltilmesi gerektiğine ve bir sonraki küçük sürümlere kadar bekleyemeyeceğine karar verdi. Bu yüzden Version 2.3.6'u NuGet ve CodePlex'e ittim. v2.3.6 bu sorunu giderir.

Çözüm:

geçici çözüm (örneğin, DeleteBaseCommand<TModel> gibi) diğer tür iç içe yerleştirilmiştir besleyen genel tür argümanları önlemektir. Bunun yerine, aşağıdaki örnekte görüldüğü gibi, jenerik tür kısıtlamaları kullanmaya geri dönebilirsiniz:

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