Reputation: 61
I have a .net core 3.1 webapi project. In some controllers, I need to execute some code that doesn't impact the response to the client.
In particular, I have a method that returns a json with a profile's information, which is called when user visits that profile's page. After get profile's information, I need to log that a user visits this page, but, for a faster response, I want to return response before this log operation.
[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);
}
}
How can I do it?
Thanks.
Upvotes: 3
Views: 2710
Reputation: 27
There are 2 possible solutions:
Task.Run()
in conjuction with ScopeFactory
.For the second option, the basic code would look like this:
[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);
}
This will crash. As soon as the controller returns it will dispose of all the services, including _logService
' leading to an error.
The solution is to use IServiceScopeFactory
to create a scoped service that will be disposed when the task ends:
[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);
}
For a complete example of this implementation see this post.
Upvotes: 1
Reputation: 147
You can use HangFire or Quartz Scheduler to run task in background without waiting response.
Example using HangFire :
var jobId = BackgroundJob.Enqueue(
() => _logService.LogProfileVisit(id));
Upvotes: 0
Reputation: 346
You can use many approach but there is no single line solution for that. You can use background workers to achive that. "Bakcground service with hosted Service in .NET Core" may suit your needs. You can also use a library like Hangfire for easily configurable complicated solutions.
Upvotes: 0
Reputation: 5861
Remove await
keyword before _logService.LogProfileVisit(id);
This will prevent you from waiting for response
[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);
}
}
Upvotes: 1