【问题标题】:Generic Async Query Class通用异步查询类
【发布时间】:2014-08-15 10:35:15
【问题描述】:

基于this梦幻般的CQRS文章,我定义了以下接口:

public interface IQuery<TResult>
{
}

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

实现通用异步 QueryHandler 的最佳方式是什么?到目前为止,我有一个新的接口定义来将 Handle() 方法更改为 void 并接受新的“完成”事件处理程序。比如:

public interface IQueryHandlerAsync<TQuery, TResult> where TQuery : IQuery<TResult>
{
    void Handle(TQuery query, RunWorkerCompletedEventHandler complete);
}

我可以为所有实现 IQueryHandlerAsync 接口的查询处理程序创建新的具体类,例如:

public class FindUsersBySearchTextQueryHandlerAsync
    : IQueryHandlerAsync<FindUsersBySearchTextQuery, User[]>
{
    private readonly IQueryHandler<FindUsersBySearchTextQuery, User[]> queryHandler;

    public FindUsersBySearchTextQueryHandler(IQueryHandler<FindUsersBySearchTextQuery, User[]> queryHandler)
    {
        this.queryHandler = queryHandler;
    }

    public void Handle(FindUsersBySearchTextQuery query, RunWorkerCompletedEventHandler complete)
    {
        var worker = new BackgroundWorker();
        worker.DoWork += DoWork;
        worker.RunWorkerCompleted += complete; 
        worker.RunWorkerAsync(query);        
    }

    void DoWork(object sender, DoWorkEventArgs e)
    {
        FindUsersBySearchTextQuery query = (FindUsersBySearchTextQuery)e.Argument;
        queryHandler.Handle(query);
    }
}

这个 Async 具体类对于我的所有查询都非常相似。有没有办法使这个通用并且可能自动注入?

编辑:解决方案

保留接口,但更改 Handle() 方法以返回任务:

public interface IQuery<TResult> { }

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    Task<TResult> Handle(TQuery query);
}

在具体实现中,将 Handle() 方法的工作封装到一个 Task 中,启动该任务,然后返回它。

public Task<User[]> Handle(FindUsersBySearchTextQuery query)
{
    return TaskEx.Run(() =>
    {
        //Do slow operation
        //...
        return new User[] { ... };
    });
}

用法:

private async void GetUsers()
{
    var users = await queryHandler.Handle(new FindUsersBySearchTextQuery
    {
        SearchText = searchText
    });
}

【问题讨论】:

  • 为什么不尝试使用/应用 .net 4.5 的 await/async 功能?
  • 不幸的是,为了兼容 WinXP,我一直坚持使用 .NET 4.0。
  • @BoroDrummer 它将对象定义为一个查询(-definition),它将返回一个指定返回类型的对象。

标签: c# generics cqrs simple-injector


【解决方案1】:

你不需要做任何事情。只需返回Task&lt;T&gt;

public class FindUsersBySearchTextQuery : IQuery<Task<User[]>>
{
}

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, Task<User[]>>
{
    public async Task<User[]> Handle(FindUsersBySearchTextQuery query)
    {
        // ...
    }
}

Task&lt;T&gt; 是 .NET 4.0 的一部分,因此您可以使用它。您可以使用 C# 5.0,这允许您使用新的 async/await 关键字,这使编程变得更加容易。如果这不可用,您仍然可以使用 .NET 4.0 中的 TPL API 来实现这一点。

另一方面,如果您确定所有查询始终是异步的,则可以将接口更改为以下内容:

public interface IQuery<TResult> { }

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    // Note the Task<T> here
    Task<TResult> Handle(TQuery query);
}

这种方法通常更好,因为现在您将有关事物是否异步的知识移出查询定义。查询定义(例如class FindUsersBySearchTextQuery : IQuery&lt;User[]&gt;)应该只关心返回哪些数据,是否异步是一个实现细节(不幸的是它是一个实现细节infects your call stack like a virus,但仍然是一个实现细节)。

【讨论】:

  • 在第二种情况下,你是说具体的 Handle() 方法应该只返回一个 Task 而不是异步的吗?然后由调用者负责决定是否同步处理任务。
  • @BoroDrummer:如果您返回 Task,则处理程序变为异步。但与往常一样,由客户端决定它是否在后台运行,或者客户端 ContinuesWith 某个委托。但这适用于所有异步代码。
  • 谢谢。我已经用我看到的解决方案编辑了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 2016-08-12
  • 1970-01-01
  • 1970-01-01
  • 2014-04-24
相关资源
最近更新 更多