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

时间:2017-09-22 07:55:39

标签: c# asp.net-mvc asp.net-core domain-driven-design cqrs

我想创建泛型查询处理程序(以及将来的命令处理程序),它可以处理每个查询,并在处理后返回查询结果。

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 inteface(标记接口):

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();
    }
}

QueryBus

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

当然还有Program.cs类(我使用控制台应用程序来测试)

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

        bus.Resolve<BrowseTitlesQueryHandler>(query);

    }
}

在我看来它应该有效,但它没有。 我有以下错误:

  

类型&#39; cqrs.BrowseTitlesQueryHandler&#39;不能用作类型参数&#39; T&#39;在通用类型或方法&#39; QueryBus.Resolve(IQuery)&#39;。来自&#39; cqrs.BrowseTitlesQueryHandler&#39;没有隐式引用转换。 to&#39; cqrs.IQueryHandler&#39;。 [CQRS]

为什么?

2 个答案:

答案 0 :(得分:4)

此处co- and contravariance存在问题。

让我们首先看一下协方差:BrowseTitlesQueryHandler实现IQueryHandler<BrowseTitlesQuery, IEnumerable<string>>所以Execute的返回值是IEnumerable<string>。但是,在QueryBus中,您期望T IQueryHandler<IQuery, object>的返回值为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);

答案 1 :(得分:0)

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

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);