2017-02-16 132 views
8

我有.NET核心Web API作为服务层。服务层具有全部EF代码。在webapi中使用async等待的最佳做法

如果有basecontroller与此代码

protected Task<IActionResult> NewTask(Func<IActionResult> callback) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     try 
     { 
      return callback(); 
     } 
     catch (Exception ex) 
     { 
      Logger.LogError(ex.ToString()); 
      throw; 
     } 
    }); 
} 

在控制器动作我包裹的所有呼叫在上述方法中例如以服务:

[HttpGet("something")] 
public async Task<IActionResult> GetSomething(int somethingId) 
{ 
    return await NewTask(() => 
    { 
     var result = _somethingService.GetSomething(somethingId); 

     if (result != null) 
      return Ok(result); 
     else 
      return NotFound("Role not found"); 
    }); 
} 

正在考虑明天这个正确的方式我可以在行动不止一个服务电话或拨打电话给其他Web服务。请指教。

+4

能否请你给你正试图在这里完成的更多信息?您的代码现在基本上通过在后台线程上执行同步工作来伪造异步工作。更何况使用Task.Factory.StartNew是非常危险的,你应该使用Task.Run来代替,但你甚至不需要Task.Run在这里。 –

+0

某些服务有一些使用entityframework核心的crud操作。也明天我可能有一个名为feedService的服务,它将使用httpclient调用外部Web获取提要。我希望我的api能够从异步中获益。等待上面的模式将满足这些需求+任何问题或改进。 – krishna

回答

17

我希望我的API从异步中获益等待上面的图案thing.does将满足这些需求

不,不。在线程池上运行同步工作可为您提供同步异步代码的缺点,但两者的优点都不同。

什么服务都有其使用的EntityFramework核心

目前一些CRUD操作,你的操作方法就是我所说的“假异步” - 它看起来异步(例如,使用await),但实际上是只是在后台线程上运行阻止代码。在ASP.NET上,你需要真正的异步,这意味着你必须一直是异步的。有关ASP.NET为什么这么糟糕的更多信息,请参阅我的intro to async on ASP.NET article的前半部分(它主要处理ASP.NET非内核,但第一部分讨论同步或异步请求对任何类型的服务器均有效)。

为了使这个真正的异步,你应该从最低层开始 - 在这种情况下,你的EFCore调用。他们都支持异步。因此,请将x.FirstOrDefault()等API调用替换为await x.FirstOrDefaultAsync()(对于您所有的创建/更新/删除等操作也是如此)。

然后允许async/await从那里自然地增长;编译器会引导你。你最终会与你somethingService异步方法可以消耗掉这样的:

[HttpGet("something")] 
public async Task<IActionResult> GetSomething(int somethingId) 
{ 
    var result = await _somethingService.GetSomethingAsync(somethingId); 
    if (result != null) 
    return Ok(result); 
    else 
    return NotFound("Role not found"); 
} 
+0

如果核心异步呼叫比同步呼叫慢,请告知。 – krishna

+0

@ user1603828:差异应该可以忽略不计。如果这不是你所看到的,我建议与EF团队联系。 –

+0

正在通过你的博客有很多有趣的信息。 – krishna

6

好吧,首先,你应该停止使用Task.Factory.StartNew和使用Task.Run只有当你有大量CPU限制的工作,你想要在线程池线程上运行。在你的情况下,你根本不需要这些。另外你应该记住,你应该只在调用方法时使用Task.Run,而不是在方法的实现中使用。你可以阅读更多关于here

在你的情况下,你真正想要的是在你的服务中有异步工作(我真的不确定你甚至需要服务在你的情况下),当你实际调用数据库并且你想使用异步/等待,而不是只在后台线程上运行一些东西。

基本上你的服务应该是这个样子(如果你确信你需要服务):

class PeopleService 
{ 
    public async Task<Person> GetPersonByIdAsync(int id) 
    { 
     Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id); 
     return randomPerson; 
    } 
} 

正如你可以看到你的服务现在使异步调用数据库,这基本上是你的模式应该是。您可以将其应用于所有操作(添加/删除/等等)。

在使您的服务异步后,您应该能够轻松地使用操作中的数据。

你的行为应该是这个样子:

[HttpGet("something")] 
public async Task<IActionResult> GetPerson(int id) 
{ 
    var result = await PeopleService.GetPersonByIdAsync(id); 

    if (result != null) 
     return Ok(result); 
    else 
     return NotFound("Role not found"); 
} 
+0

我做了一些有关ef核心异步方法的研究,并表示它们比同步ef方法慢。请指教。 – krishna

+0

@kirshna异步方法比较慢。当它们同步运行时,它们具有不可避免的开销。但他们可以增加吞吐量。因此,您应该考虑吞吐量和性能之间的平衡。性能问题总是如此,这很复杂。 https://www.dotnetrocks.com/?show=1433给这个听,你会看到我在说什么。 – thinklarge