【问题标题】:Wrap async lambda to the async method将 async lambda 包装到 async 方法
【发布时间】:2016-02-08 17:20:07
【问题描述】:

我正在尝试将我的 Operations Contracts 包装到 try-catch 块中。问题是我的 EF 上下文在同一时间范围内被破坏。我猜问题编译器不知道该怎么做。我的 catch 块也不处理任何异常。对不起我的英语,我希望我的代码能说明我的意思:

private static async Task<T> SurroundWithTryCatch<T>(Action t, T response) where T : BaseResponse 
{
    try
    {
        await Task.Run(t);
    }
    catch (Exception e)
    {
        response.Success = false;
        response.Errors.Add(e.Message);
    }
    return response;
}

public async Task<CreateResponse> CreateAsync(CreateRequest request)
{
    var response = new CreateResponse();
    return await SurroundWithTryCatch(async delegate
    {
        var newUser = new ApplicationUser { UserName = request.UserName, Email = request.Email };
        await Database.UserManager.CreateAsync(newUser, request.Password);
        //Some another logic...
        await Database.CommitAsync();
    }, response);
}

CreateAsync方法第二行的问题。 UserManager 之前被 GC 清理过。所以我有ObjectDisposedExceptionDatabaseIUnitOfWork 的实现,由Autofac 注入。

【问题讨论】:

  • 尝试将SurroundWithTryCatch&lt;T&gt;(Action t, T response) 替换为SurroundWithTryCatch&lt;T&gt;(Func&lt;Task&gt; t, T response),将return await SurroundWithTryCatch(async delegate 替换为return await SurroundWithTryCatch(async () =&gt;
  • @YacoubMassad 感谢您的快速回复!有用!!我在两点钟或更长时间里挣扎。您能否解释发生了什么并将其发布为答案?

标签: c# wcf asynchronous async-await


【解决方案1】:

你打破了await 链 - t 不再返回任务。由于您不再有任务,因此将在第一个 await 之后继续执行,而不是在整个方法完成之后。将await 视为return - 如果您不返回(和await/等待)任务,您将失去同步的机会。

相反,您想传递Func&lt;Task&gt;,并直接等待它:

private static async Task<T> SurroundWithTryCatch<T>(Func<Task> t, T response) where T : BaseResponse 
{
    try
    {
        await t();
    }
    catch (Exception e)
    {
        response.Success = false;
        response.Errors.Add(e.Message);
    }
    return response;
}

public async Task<CreateResponse> CreateAsync(CreateRequest request)
{
    var response = new CreateResponse();
    return await SurroundWithTryCatch(async () =>
    {
        var newUser = new ApplicationUser { UserName = request.UserName, Email = request.Email };
        await Database.UserManager.CreateAsync(newUser, request.Password);
        //Some another logic...
        await Database.CommitAsync();
    }, response);
}

Task.Run 也可以,但你可能不希望这样 - 你的代码是异步的,并且(猜测)你在 ASP.NET 请求中运行,所以启动一个任务只是为了等待另一个任务的结果。 Task.Run 用于在单独的线程中执行 CPU 工作,这在 ASP.NET 中通常要避免。

【讨论】:

  • 非常感谢!你今天救了我的命!
  • 如果我的T 是前置类型,我可以做同样的事情吗?
  • 我有一个包装原始类型的想法,但可能存在更好的方法。
  • @user3818229 我不确定您所说的“原始类型”到底是什么意思。一般来说,泛型适用于您想要的任何类型。如果你想做一些更具体的事情,你可以使用泛型类型约束(就像你在这里一样)或强制转换。
  • @user3818229 所以你的意思是值类型?包装它们适用于泛型。诀窍是确保您修改的是共享值,而不是复制的值(值类型按值传递给方法,因此它们总是创建一个副本)。一种方法是使用ref T,另一种方法是创建自己的包装类。
猜你喜欢
  • 1970-01-01
  • 2018-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 2015-10-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多