我想创建泛型查询处理程序(以及将来的命令处理程序),它可以处理每个查询,并在处理后返回查询结果。
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]
为什么?
答案 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);