【问题标题】:How do I return the TaskStatus when making a synchronous call asynchronous?进行异步同步调用时如何返回 TaskStatus?
【发布时间】:2017-01-22 00:40:14
【问题描述】:

我有一个旧的数据访问库,我需要在我的 ASP.NET MVC 应用程序中使用它,但我无法将它带入 MVC 世界,在该世界中,许多服务器端操作预计是异步的。我的数据访问库如下所示:

public class MyOldDAL
{
    public TaskResult CreateUser(string userName)
    {
        try
        {
            // Do user creation
            return new TaskResult { Success = true, Message = "success" };
        }
        catch (Exception ex)
        {
            return new TaskResult { Success = false, Message = ex.Message };
        }
    }
}

并由我的 MVC 应用程序调用,如下所示:

public class MyUserStore : IUserStore<ApplicationUser>
{
    private readonly MyOldDAL _dal = new MyOldDAL();
    public async Task CreateAsync(ApplicationUser user)
    {
        await Task.Run(() => _dal.CreateUser(user.UserName));
    }
}

这很好,直到Task.Run() 中的方法由于某种原因失败。如果TaskResult.Success 为假,我希望能够返回TaskStatus.Faulted,但我无法在网上找到如何正确执行此操作的示例-我所有的谷歌搜索都是如何使用Task&lt;T&gt;,@ 987654327@ 接口不允许。

为了记录,尽管我很想更改 MyOldDAL - 它的目标是 .NET 3.5,所以那里没有 asyncawait

【问题讨论】:

  • 如果Successfalse,为什么不抛出异常?
  • 我刚刚尝试过 - 它有效,但它似乎不是一种特别优雅的方式!

标签: c# .net asp.net-mvc async-await


【解决方案1】:

注意: 正如 Stephen Cleary 在他的回答中指出的那样,Task.Run 不应该在 ASP.NET 上使用。

原始答案(在 cmets 之前): 你的CreateAsync 方法通常应该是这样的:

public async Task<TaskResult> CreateAsync(ApplicationUser user)
{
    return await Task.Run(() => _dal.CreateUser(user.UserName));
}

但是如果你不能从CreateAsync 方法返回Task&lt;TaskResult&gt;...那么你就不能从CreateAsync 获得TaskResult 定义。在这种情况下,您可以将结果存储在本地:

private TaskResult taskResult;

public async Task CreateAsync(ApplicationUser user)
{
    var result = await Task.Run(() => _dal.CreateUser(user.UserName));
    this.taskResult = result;
    // process taskResult wherether you need
}

或使用TaskResult 有效载荷引发事件,允许MyUserStore 的客户端订阅此事件:

public event EventHandler<TaskResult> TaskCompleted;

public async Task CreateAsync(ApplicationUser user)
{
    var result = await Task.Run(() => _dal.CreateUser(user.UserName));
    this.OnTaskCompleted(result);
}

private void OnTaskCompleted(TaskResult result)
{
    this.TaskCompleted?.Invoke(this, result);
}

【讨论】:

  • 抱歉,我应该明确表示我正在替换 ASP.NET Identity 的一部分,并且我没有编写调用 CreateAsync 的代码。 MyUserStore 是否知道失败,我不介意,但无论调用 CreateAsync 都必须知道操作是否成功。
  • 你的意思是不能改变MyUserStore类,也就是说不能改变返回类型,不能给它添加事件?在那种情况下,你能把任何参数传递给MyUserStore吗?你能编辑MyOldDAL类吗?
  • 不完全是,我无法更改CreateAsync 的方法签名,因此我无法将返回类型更改为Task&lt;T&gt; 或传入额外的参数。 MyUserStore 的其余部分具有更大的灵活性。
  • 您是使用MyUserStore的单个实例,还是可以使用多个实例并且必须区分它们?
  • 使用async Task CreateAsync(ApplicationUser user)签名你无法获得TaskResult,这是不可能的,因为它返回无效。可以修改ApplicationUser类吗?
【解决方案2】:

报告任务错误的常规方法是通过异常,因此您只需要自己进行转换:

public class MyUserStore : IUserStore<ApplicationUser>
{
  private readonly MyOldDAL _dal = new MyOldDAL();
  public Task CreateAsync(ApplicationUser user)
  {
    var result = _dal.CreateUser(user.UserName);
    if (result.Success)
      return Task.CompletedTask;
    return Task.FromException(new InvalidOperationException(result.Message));
  }
}

请注意,Task.Run 不应在 ASP.NET 上使用。

【讨论】:

  • 太好了,感谢您澄清例外情况。我不知道Task.CompletedTask 等,所以我会改用它。 Task.Run 没有在 ASP.NET 中使用或者只是约定俗成,是否有技术原因?
  • @Mourndark:主要原因是 ASP.NET 上的Task.Run 不必要地低效。它还(临时)在请求上下文“外部”执行,例如,如果 MyOldDAL.CreateUser 依赖于请求的用户进行部分处理(如权限检查),它将失败。
猜你喜欢
  • 2017-09-09
  • 1970-01-01
  • 2014-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 1970-01-01
相关资源
最近更新 更多