我有.NET核心Web API作为服务层。服务层包含所有EF代码。
如果有这个代码的基本控制器
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服务。请指教。
答案 0 :(得分:20)
我希望我的api能够从异步等待事物中获益。以上模式将满足这些需求
不,它没有。在线程池上运行同步工作会给你带来同步和异步代码的缺点,但两者都没有。
某些服务有一些使用entityframework核心的crud操作
目前,您的操作方法就是我所说的&#34;假异步&#34; - 它看起来是异步的(例如,使用x.FirstOrDefault()
),但实际上只是在后台线程上运行阻塞代码。在ASP.NET上,您需要真正的异步,这意味着您必须始终保持异步。有关为什么这对ASP.NET不好的更多信息,请参阅我的intro to async
on ASP.NET article的前半部分(它主要处理ASP.NET非核心,但第一部分讨论同步和异步请求对任何类型都有效)服务器)。
要使这真正异步,您应该从最低级别开始 - 在这种情况下,您的EFCore调用。他们都支持异步。因此,将await x.FirstOrDefaultAsync()
之类的API调用替换为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");
}
上使用异步方法,这些方法可以这样使用:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T> { ... }
答案 1 :(得分:6)
好的,首先,只有当你想要在线程池线程上运行大量的CPU工作时,才应该停止使用Task.Factory.StartNew
并使用Task.Run
。在你的情况下,你根本不需要它。此外,您应该记住,在调用方法时应该只使用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");
}