我们将从一个独立的故事开始,只是为了让您了解原因: 我想针对同一界面处理任何更改数据的操作:ICommand 那里存在称为 ICommandHandlers 的东西,可以处理我想要的任何命令。 所以,如果我想要一个 CreatePersonCommand,我需要一个 CreatePersonCommandHandler。
所以这是一个控制台应用程序的主体,它演示了这一点:(需要 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
}
}
所以无论出于何种原因,SimpleInjector 总是尝试解析 DeleteCommandHandler<>
对于 CreateBaseCommand<>
我有。
同样,顺序无关紧要。
我还有其他的封闭式命令处理程序(及其各自的命令),它们只是继承了 ICommandHandler<,>
哪个工作正常。
我花了很多时间从 this 中了解所有可能的注册类型.
最佳答案
更新:
这绝对是当前版本中的错误。这不知何故从单元测试裂缝中溜走了。该代码遗漏了一个检查,该检查验证构建的封闭式通用实现是否实际实现了请求的封闭式通用服务类型。如果所有通用类型约束都有效,则框架认为解析成功,这在您的情况下是不正确的。
修复相当容易,即将推出的 v2.4 肯定会修复此问题,但与此同时,您必须使用以下解决方法。
更新 2:
这实际上非常讨厌,在某些情况下很难解决。除了 RegisterOpenGeneric 之外,装饰器注册也会受到影响。这让我得出结论,这必须快速修复,不能等到下一个次要版本。因此我推了Version 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);
}
}
关于c# - RegisterOpenGeneric with SimpleInjector 解析不正确的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19458272/