【问题标题】:Accessing an async method from a Base Class从基类访问异步方法
【发布时间】:2019-06-03 04:21:34
【问题描述】:

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

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. 这两种方法是否都被认为是正确的,它们是否都符合 async/await 的处理方式?
  2. 这两种方法能完成同样的事情吗?
  3. 这两种方法中的任何一种都有它们的缺点吗?
  4. 如果有人发现这些方法有任何错误,您能否提出一种更好/更清洁的方法来实现我的目标。

【问题讨论】:

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


    【解决方案1】:

    关于这有什么问题的多个帖子。让我们看看如何解决它:

    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);
        ...
    }
    

    【讨论】:

    • 谢谢 Henk,这正是我最终所做的。但是给你一个问题 - 我可以像这样调用 GetLoggedInUserId 内联 - _userService.CreateUser(userCreate, await GetLoggedInUserId()) 而不是在外面调用 GetLoggedInUserId() 方法吗?
    • 是的,很容易尝试。但这纯粹是装饰性的,我不介意额外的变量。
    【解决方案2】:

    这两种方法是否都被认为是正确的,它们是否都符合 async/await 的做事方式?

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

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

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

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

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


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

    【讨论】:

    • 有更好的方法,如果使用 ASP.NET Core Identity,或者使用中间件
    【解决方案3】:
    1. 这两种方法是否都被认为是正确的,它们是否都符合 async/await 的处理方式?

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

    1. 这两种方法能完成同样的事情吗?

    不,第一种方法会创建一个不需要的线程来同步阻塞它。如果您使用的是使用 SynchronizationContext 的框架(想想经典的 ASP.NET、WinForms、Xamarin 或 WPF),您将面临死锁。然而,第二种方法是进行异步调用的正确方法。在这两种情况下,您最终都会得到用户 ID,至少现在是这样。

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

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

    1. 如果有人发现这些方法有任何错误,您能否提出一种更好/更清洁的方法来实现我的目标。

    好吧,您没有提到您使用什么进行身份验证/授权,但如果您使用的是 ASP.NET Core Identity,它已经通过 UserManager&lt;TUser&gt; 类为您提供了该功能。否则,您可以使用读取请求并将所需数据添加到控制器的中间件。

    【讨论】:

    • 对不起,我正在使用 IdentityServer 进行身份验证。所以你在我发送的代码 sn-ps 中没有看到的是我将“HttpContextAccessor”单例服务注入“BaseController”。从那里,我获取“HttpContext.User”对象并获取我需要的声明。就我而言,我需要用于查询我的数据库以获取用户的唯一标识符的“子”。所以从你的意思来看,我可以通过一些自定义中间件或身份服务器提供的“UserManager”类异步完成所有这些工作?
    • @BryMan 不,Identity Server 是在 ASP.NET Core 上运行的开源产品,但它不是 ASP.NET Core Identity(这是 Microsoft 产品)。但是,您根本不需要HttpContextAccesor,这仅适用于不存在于请求中的类,而控制器实例始终存在于请求中。您可以只使用(在实例方法中)var userId = User.Claims...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多