【问题标题】:.net core 3 web api controller - code execution after sending response.net core 3 web api控制器-发送响应后执行代码
【发布时间】:2020-04-04 15:33:42
【问题描述】:

我有一个 .net core 3.1 webapi 项目。在某些控制器中,我需要执行一些不会影响对客户端的响应的代码。

特别是,我有一个方法可以返回带有个人资料信息的 json,当用户访问该个人资料的页面时会调用该方法。获取配置文件信息后,我需要记录用户访问此页面,但为了更快的响应,我想在此日志操作之前返回响应。

[Route("[controller]")]
[ApiController]
public class ProfilesController : ControllerBase
    {
        [HttpGet("{id}")]
        public async Task<ActionResult<Profile>> GetProfileById([FromRoute] int id)
        {
             Profile profile = await _profileService.GetByIDAsync(id);

             // DO THIS OPERATION IN BACKGROUND AND DON'T WAIT TO RETURN RESPONSE
              await _logService.LogProfileVisit(id);

             return Ok(profile);
        }
}

我该怎么做?

谢谢。

【问题讨论】:

  • 只需删除await,您不再等待回复。
  • 如果_logService 是Scoped,您不能只删除await。因为它将在请求处理结束时被销毁。

标签: c# .net-core asp.net-core-webapi


【解决方案1】:

删除_logService.LogProfileVisit(id);之前的await关键字

这将阻止您等待响应

[Route("[controller]")]
[ApiController]
public class ProfilesController : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<ActionResult<Profile>> GetProfileById([FromRoute] int id)
    {
        _logService.LogProfileVisit(id);
        Profile profile = await _profileService.GetByIDAsync(id);
        return Ok(profile);
    }
}

【讨论】:

  • 您好 Farhard,感谢您的回复。我尝试了这个解决方案,但它不起作用,因为我认为当 webapi 返回响应时,数据库的上下文(我使用实体框架)被释放,因此未完成挂起的操作。
  • @Marco 当然。在 .net core DI 中处理 DbContext 后执行操作。如果LogProfileVisitDbContext 一起使用,可能会发生错误
【解决方案2】:

例如,您可以使用过滤器来执行此操作。阅读here

【讨论】:

    【解决方案3】:

    您可以使用多种方法,但没有单行解决方案。您可以使用后台工作人员来实现这一点。 “.NET Core 中带有托管服务的 Bakcground 服务”可能适合您的需求。您还可以使用像 Hangfire 这样的库来轻松配置复杂的解决方案。

    【讨论】:

      【解决方案4】:

      您可以使用HangFireQuartz Scheduler 在后台运行任务而无需等待响应。

      使用 HangFire 的示例:

      var jobId = BackgroundJob.Enqueue(
          () => _logService.LogProfileVisit(id));
      

      【讨论】:

        【解决方案5】:

        有两种可能的解决方案:

        1. 使用提供此功能的框架,例如 HangFire 或 Quartz Scheduler。如果您需要在多个地方使用此功能,这可能是有意义的。否则,它们会很重。
        2. Task.Run()ScopeFactory 结合使用。

        对于第二个选项,基本代码如下所示:

        [HttpGet("{id}")]
        public async Task<ActionResult<Profile>> GetProfileById([FromRoute] int id)
        {
             Profile profile = await _profileService.GetByIDAsync(id);
        
             Task.Run(async () =>
             {
                 await _logService.LogProfileVisit(id);
             }
        
             return Ok(profile);
        }
        

        这会崩溃。一旦控制器返回,它将处理所有服务,包括导致错误的_logService'。 解决方案是使用IServiceScopeFactory 创建一个作用域服务,该服务将在任务结束时释放:

        [HttpGet("{id}")]
        public async Task<ActionResult<Profile>> GetProfileById([FromRoute] int id)
        {
             Profile profile = await _profileService.GetByIDAsync(id);
        
             Task.Run(async () =>
             {
                 using var scope = _serviceScopeFactory.CreateScope();
                 var _logService = scope.ServiceProvider.GetService<LogService>();
                 await _logService.LogProfileVisit(id);
             }
        
             return Ok(profile);
        }
        

        有关此实现的完整示例,请参阅post

        【讨论】:

          猜你喜欢
          • 2011-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-17
          • 2020-08-06
          相关资源
          最近更新 更多