从基类访问异步方法

时间:2019-06-02 17:24:07

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

我有一个.NET Core 2.2 Web Api项目,我在其中尝试使用async / await方法进行所有操作。我有一个名为“ UserController”的控制器。我的“ UserController”中有一些方法需要访问LoggedInUserId。因此,为实现此目的,我创建了一个“ BaseController”,“ UserController”将继承自此。

BaseController.cs

public abstract class BaseController : ControllerBase
{
    public int LoggedInUserId
    {
        get
        {
           Task<int> task = Task.Run(async () => await GetLoggedInUserId());
           return task.Result;
        }
    }
}

“ BaseController”中有一个名为“ GetLoggedInUserId()”的私有方法,该方法将调用数据库(这就是为什么该方法需要异步)并检索我需要的信息的原因。

所以,在理解之后,这是我的“ UserController”

UserController.cs

[Route("api/[controller]")]
[ApiController]
public class UsersController : BaseController
{
    public UsersController() 
    {

    }

    [HttpPost("create")]
    public async Task<ActionResult<User>> Create([FromBody] userCreate)
    {
        _userService.CreateUser(userCreate, LoggedInUserId);
    }
}

如您所见,我正在从“ BaseController”访问“ LoggedInUserId”属性,但我担心的是,因为“ BaseController”中的“ LoggedInUserId”属性返回一个“ task.Result”,它不是真正的异步对象

所以,我想到了另一种方法,那就是从“ BaseController”中删除“ LoggedInUserId”属性,并使“ GetLoggedInUserId()”方法成为公共方法,而不是私有方法,并按如下方式进行访问:

UserController.cs

_userService.CreateUser(userCreate, await GetLoggedInUserId());

所以我的问题如下:

  1. 这两种方法都被认为是正确的,并且都符合异步/等待方式吗?
  2. 这两种方法都完成同一件事吗?
  3. 这两种方法中的任何一种都有缺点吗?
  4. 如果有人发现这些方法有任何缺陷,您可以建议一种更好/更干净的方法来实现我的目标。

3 个答案:

答案 0 :(得分:2)

  

这两种方法都被认为是正确的,并且都符合异步/等待方式吗?

不。使用Task.RunResult并未按设计使用async / awaitTask.Run部分是多余的。

  

这两种方法都完成相同的事情吗?

他们都获得了登录用户。但是,基于Result的方法会同时阻塞线程。

  

这两种方法中的任何一种都有缺点吗?

是的。基于Result的方法会阻塞线程,这限制了您的可伸缩性。


在我看来,这样做有更好的方法。我对ASP.NET Core管道并不完全熟悉,但是应该有一种方法可以异步处理身份验证并在构造控制器之前获取登录的用户ID。然后,您可以将其公开为属性,因为到那时它已经被加载。

答案 1 :(得分:2)

  
      
  1. 这两种方法都被认为是正确的,并且都符合异步/等待方式吗?
  2.   

否,除非您有明确的理由使用Task.Result,否则第一种方法不是正确的方法,即使那样,Task.Run也不需要调用用Task声明的方法。

  
      
  1. 这两种方法都完成同一件事吗?
  2.   

否,第一个方法创建一个不需要的线程以在其上同步阻塞。如果您使用的是使用SynchronizationContext的框架(例如经典的ASP.NET,WinForms,Xamarin或WPF),那么您将面临死锁。但是,第二种方法是进行异步调用的正确方法。在这两种情况下,至少到现在,您都将以用户ID结尾。

  
      
  1. 这两种方法中的任何一种都有缺点吗?
  2.   

是的,如上所述,第一种方法只会引起资源浪费。

  
      
  1. 如果有人发现这些方法有任何缺陷,您可以建议一种更好/更干净的方法来实现我的目标。
  2.   

好吧,您没有提到用于身份验证/授权的内容,但是如果您使用的是ASP.NET Core Identity,它已经通过UserManager<TUser>类为您提供了该功能。否则,您可以使用中间件来读取请求并将所需的数据添加到Controller。

答案 2 :(得分:2)

有关此问题的多篇文章。让我们看看如何解决它:

public abstract class BaseController : ControllerBase
{
    public Task<int> LoggedInUserId()
    {
        return await GetLoggedInUserId();
    }
}

或仅使GetLoggedInUserId()可访问。你明白了。然后:

[HttpPost("create")]
public async Task<ActionResult<User>> Create([FromBody] userCreate)
{
     int loggedInUserId = await LoggedInUserId();
    _userService.CreateUser(userCreate, loggedInUserId);
    ...
}