尝试使用 Ninject 工厂扩展解决依赖性时出错

Error trying to resolve dependency using Ninject Factory extensions

我有一个 Web API 项目,我试图在给定查询类型和查询结果类型的情况下在运行时解析查询处理程序。下面是我用作概念证明的一些测试代码。我正在使用 Ninject 工厂扩展,但我无法获得正确的设置,因为我想 return 一个通用接口,也许这是错误的设计,所以任何建议将不胜感激。

代码

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); 时有效,但是当我尝试使用下面的工厂时它失败了

NinjectWeb常用代码:

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]'.

我可能把事情复杂化了,或者这有可能吗?我希望它是。有没有人有任何建议让这个工作?

我会说它根本行不通。

问题是动态创建的工厂使用 provider.GetInstance 方法来 return 您的对象。然后它尝试将其转换为目标接口。

这意味着在您的示例中它执行以下操作:

var obj = // your PaymentQueryHandler object
(IQueryHandler<IQuery, IQueryResult>)obj

但它不会工作,因为您的界面无法通过应用 out modifier.

启用 covariance

但即使在这里你也有问题。您的接口使用第一个通用参数和输入类型,第二个通用参数作为输出参数。这意味着您需要启用 contravariance。但在那种情况下,您将无法转换您的类型,因为您需要将其转换为更多派生类型。

这就是为什么它不适用于您的工厂。

并且它适用于标准 get 调用,因为它知道目标类型并且不使用转换为 IQueryHandler<IQuery, IQueryResult>

在我看来,您确实把事情复杂化了。工厂的意义何在?如果 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);
    }
}