我有一个Web API项目,我试图在给定查询类型和查询结果类型的情况下在运行时解析查询处理程序。下面是我用作概念证明的一些测试代码。我正在使用Ninject Factory扩展,但我无法获得正确的设置,因为我想返回一个通用接口,也许这是错误的设计所以任何建议都会感激不尽。
代码
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;
}
}
我现在只是在我的NinjectWebCommon中测试一些Ninject调用,看看它是否可行。我希望能够使用MakeGenericType
方法,因为我知道查询类型和结果类型。它在我调用var test = kernel.Get(queryHandlerType);
时有效但是当我尝试使用下面的工厂
NinjectWebCommon代码:
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
生成的错误低于
Exception thrown: 'System.InvalidCastException' in DynamicProxyGenAssembly2
Additional information: Unable to cast object of type 'PaymentQueryHandler' to type 'IQueryHandler`2[IQuery,IQueryResult]'.
我可能会让事情变得复杂,或者这甚至可能吗?我会这样的。有没有人建议让这个工作?
答案 0 :(得分:0)
我会说它根本不会工作。
问题是动态创建的工厂使用provider.GetInstance
方法返回您的对象。然后它尝试将其转换为目标接口。
这意味着在您的示例中,它执行以下操作:
var obj = // your PaymentQueryHandler object
(IQueryHandler<IQuery, IQueryResult>)obj
但它不会起作用,因为您的界面无法通过应用out modifier
来启用covariance。
但即使在这里你也有问题。您的接口使用第一个泛型参数和输入类型以及第二个泛型参数作为输出参数。这意味着您需要启用contravariance。但在这种情况下,您无法转换您的类型,因为您需要将其转换为更多派生类型。
这就是为什么它不与你一起工作。
它适用于标准get
调用,因为它知道目标类型并且不使用强制转换为IQueryHandler<IQuery, IQueryResult>
。
答案 1 :(得分:0)
在我看来,你确实过度复杂。工厂的重点是什么?如果kernel.Get(queryHandlerType)
有效,为什么不使用它?
这个非常简单的实现怎么样:
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);
}
}