没有来自...通用查询处理程序 cqrs 的隐式引用转换

There is no implicit reference conversion from... generic query handler cqrs

我想创建一个通用的查询处理程序(以及未来的命令处理程序),它可以处理每个查询并在处理后,returns 查询结果。

IQueryHandler 接口:

public interface IQueryHandler
{

}

public interface IQueryHandler<TResult> : IQueryHandler
{
    TResult Execute();
}

public interface IQueryHandler<TQuery, TResult> : IQueryHandler
    where TResult : class 
    where TQuery: class
{
    TResult Execute(TQuery query);
}

IQuery接口(标记接口):

public interface IQuery
{

}

简单查询对象:

public class BrowseTitlesQuery : IQuery
{
    public string Title { get; set; }
}

简单查询处理程序对象:

public class BrowseTitlesQueryHandler : IQueryHandler<BrowseTitlesQuery, IEnumerable<string>>
{
    public IEnumerable<string> Execute(BrowseTitlesQuery query)
    {
        throw new System.NotImplementedException();
    }
}

查询总线

public class QueryBus
{
    public object Resolve<T>(IQuery query) 
        where T: IQueryHandler<IQuery, Object>, IQueryHandler, new()
    {
        return new T().Execute(query);
    }
}

当然还有Program.cs class(我正在使用控制台应用程序进行测试)

class Program
{
    static void Main(string[] args)
    {
        var bus = new QueryBus();
        var query = new BrowseTitlesQuery();

        bus.Resolve<BrowseTitlesQueryHandler>(query);

    }
}

在我看来它应该有效,但实际上无效。 我有以下错误:

The type 'cqrs.BrowseTitlesQueryHandler' cannot be used as type parameter 'T' in the generic type or method 'QueryBus.Resolve(IQuery)'. There is no implicit reference conversion from 'cqrs.BrowseTitlesQueryHandler' to 'cqrs.IQueryHandler'. [cqrs]

这是为什么?

这里 co- and contravariance 有问题。

让我们先看一下协方差:BrowseTitlesQueryHandler 实现了 IQueryHandler<BrowseTitlesQuery, IEnumerable<string>>,所以 Execute 的 return 值是 IEnumerable<string>。但是,在 QueryBus 中,您期望 TIQueryHandler<IQuery, object>,return 值为 object

为了允许 IQueryHandler<TQuery, TResult> 转换为 IQueryHandler<TQuery, object>TResult 参数需要 协变 。这在这里非常简单,因为它实际上 一个结果,所以使它协变是正确的做法(注意 out):

public interface IQueryHandler<out TResult> : IQueryHandler
{ … }

public interface IQueryHandler<TQuery, out TResult> : IQueryHandler
{ … }

另一个问题有点困难,归结为 BrowseTitlesQueryHandler 需要 BrowseTitlesQuery。但是 QueryBus.Resolve 只会给你一个一般的 IQuery。这对于 BrowseTitlesQueryHandler.

来说不够具体

不幸的是,解决这个问题的唯一方法是使查询类型也成为 Resolve 的通用类型参数:

public object Resolve<T, TQuery>(TQuery query)
    where T : IQueryHandler<TQuery, object>, new()
    where TQuery : class, IQuery
{
    return new T().Execute(query);
}

现在,BrowseTitlesQueryHandler 获得了正确的查询参数并可以正确执行。当然,你需要调整你的调用:

bus.Resolve<BrowseTitlesQueryHandler, BrowseTitlesQuery>(query);

您必须在查询总线上扩展类型参数:

public class QueryBus
{
    public object Resolve<THandler, TQuery, TResult>(TQuery query) 
        where T: IQueryHandler<TQuery, TResult>, IQueryHandler, new()
        where TResult : class 
        where TQuery: class
    {
        return new THandler().Execute(query);
    }
}

然后这样称呼它:

bus.Resolve<BrowseTitlesQueryHandler, BrowseTitlesQuery, IEnumerable<string>>(query);