尝试使用Ninject Factory扩展来解决依赖性时出错

时间:2016-06-09 16:55:41

标签: c# dependency-injection ninject

我有一个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]'.

我可能会让事情变得复杂,或者这甚至可能吗?我会这样的。有没有人建议让这个工作?

2 个答案:

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