C# 从基类访问异步方法
我有一个.NETCore2.2WebAPI项目,在这个项目中,我尝试使用异步/等待方法来完成所有工作。我有一个名为“UserController”的控制器。我的“UserController”中有一些方法需要访问LoggedInUserId。因此,为了实现这一点,我创建了一个“BaseController”,这个“UserController”将从中继承 BaseController.csC# 从基类访问异步方法,c#,asp.net-core,.net-core,async-await,C#,Asp.net Core,.net Core,Async Await,我有一个.NETCore2.2WebAPI项目,在这个项目中,我尝试使用异步/等待方法来完成所有工作。我有一个名为“UserController”的控制器。我的“UserController”中有一些方法需要访问LoggedInUserId。因此,为了实现这一点,我创建了一个“BaseController”,这个“UserController”将从中继承 BaseController.cs public abstract class BaseController : ControllerBase
public abstract class BaseController : ControllerBase
{
public int LoggedInUserId
{
get
{
Task<int> task = Task.Run(async () => await GetLoggedInUserId());
return task.Result;
}
}
}
[Route("api/[controller]")]
[ApiController]
public class UsersController : BaseController
{
public UsersController()
{
}
[HttpPost("create")]
public async Task<ActionResult<User>> Create([FromBody] userCreate)
{
_userService.CreateUser(userCreate, LoggedInUserId);
}
}
_userService.CreateUser(userCreate, await GetLoggedInUserId());
因此,我的问题如下:
任务。运行
和结果
未按设计使用异步
/等待
。Task.Run
部分是多余的
这两种方法是否完成了相同的任务
它们都获得登录用户。但是,基于结果的方法在执行此操作时会阻塞线程
这两种方法中的任何一种都有缺点吗
对。基于结果的方法会阻塞线程,这会限制您的可伸缩性
在我看来,有一种更好的方法可以做到这一点。我对ASP.NET核心管道不太熟悉,但在构建控制器之前,应该有一种异步处理身份验证和获取登录用户id的方法。然后可以将其作为属性公开,因为此时它已经加载
这两种方法都被认为是正确的吗?它们是否都符合异步/等待方式
不,第一种方法不是正确的方法,除非您有明确的理由使用Task.Result
,即使如此,Task.Run
也不需要调用用Task
声明的方法
这两种方法是否完成了相同的任务
不,第一个方法创建一个不需要的线程来同步阻塞它。如果您所在的框架使用SynchronizationContext(想想经典的ASP.NET、WinForms、Xamarin或WPF),您将面临死锁。然而,第二种方法是执行异步调用的正确方法。在这两种情况下,您最终都会得到用户id,至少现在是这样
这两种方法中的任何一种都有缺点吗
是的,如上所述,第一种方法只会造成资源浪费
如果有人发现这些方法有任何缺陷,你能提出一种更好/更干净的方法来实现我的目标吗
嗯,您没有提到您使用什么进行身份验证/授权,但是如果您使用的是ASP.NET核心标识,那么它已经通过UserManager
类为您提供了该功能。否则,您可以使用一个中间件来读取请求并将所需数据添加到控制器。关于此问题的多篇文章。让我们看看如何修复它:
public abstract class BaseController : ControllerBase
{
public Task<int> LoggedInUserId()
{
return await GetLoggedInUserId();
}
}
公共抽象类BaseController:ControllerBase
{
公共任务LoggedInUserId()
{
返回等待GetLoggedInUserId();
}
}
或者只需使GetLoggedInUserId()可访问。你明白了。然后:
[HttpPost("create")]
public async Task<ActionResult<User>> Create([FromBody] userCreate)
{
int loggedInUserId = await LoggedInUserId();
_userService.CreateUser(userCreate, loggedInUserId);
...
}
[HttpPost(“创建”)]
公共异步任务创建([FromBody]userCreate)
{
int loggedInUserId=等待loggedInUserId();
_CreateUser(userCreate,loggedInUserId);
...
}
有一个更好的方法,如果使用ASP.NET Core Identity,或者使用MiddleWareSrry,我使用IdentityServer进行身份验证。因此,在我发送的代码片段中,您没有看到我将“HttpContextAccessor”单例服务注入到“BaseController”中。从那里,我获取“HttpContext.User”对象并获取我需要的声明。在我的例子中,我需要使用“sub”来查询数据库,以获取用户的唯一标识符。因此,从您的意思来看,我可以通过一些自定义中间件或identity server提供的“UserManager”类异步完成所有这些工作?@BryMan否,identity server是一个在ASP.NET Core上运行的开源产品,但它不是ASP.NET Core identity(这是Microsoft产品)。不过,您根本不需要HttpContextAccesor
,这仅适用于不存在于请求中的类,而控制器实例始终存在于请求中。您可以使用(在实例方法中)var userId=User.cluses…
谢谢你Henk,这正是我最终所做的。但是有一个问题,我可以像这样内联调用GetLoggedInUserId-_userService.CreateUser(userCreate,wait GetLoggedInUserId())而不是在外部调用GetLoggedInUserId()方法吗?是的,很容易尝试。但这纯粹是装饰性的,我不介意额外的变量。