RegisterOpenGeneric与SimpleInjector解决了错误的类型

Mat*_*t V 5 c# generics dependency-injection inversion-of-control simple-injector

我们将从一个独立的故事开始,只是让你理解为什么:我想要处理任何针对同一个接口更改数据的操作:ICommand存在一些名为ICommandHandlers的东西,可以处理我想要的任何命令.所以,如果我想要一个CreatePersonCommand,我需要一个CreatePersonCommandHandler.

所以这是控制台应用程序的主体,它演示了这一点:(需要简单的注入器)

// 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
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,无论出于何种原因SimpleInjector总是试图解决DeleteCommandHandler<>CreateBaseCommand<>,我有.同样,订单无关紧要.我有其他的,封闭式的,commandhandlers(及其各自的命令),只是继承了ICommandHandler<,>哪些工作正常.

我花了很多时间好位通过各种可能的类型从登记我能去的这个.

Ste*_*ven 4

更新:

这绝对是当前版本中的一个错误。这不知何故漏掉了单元测试的漏洞。该代码错过了一个检查,该检查验证构建的封闭通用实现是否确实实现了请求的封闭通用服务类型。如果所有泛型类型约束均有效,则框架会认为解析成功,但在您的情况下这是不正确的。

修复相当简单,即将推出的 v2.4 肯定会修复此问题,但与此同时,您必须使用以下解决方法。

更新2:

这实际上非常令人讨厌,并且在某些情况下很难解决。除了RegisterOpenGeneric之外,装饰器注册也会受到影响。这让我得出结论,这个问题必须快速修复,不能等到下一个小版本。因此,我将版本 2.3.6推送到 NuGet 和 CodePlex。v2.3.6 修复了这个问题。

解决方法:

解决方法是防止提供嵌套到其他类型(例如您的DeleteBaseCommand<TModel>)中的泛型类型参数。相反,您可以转而使用泛型类型约束,如以下示例所示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)