Proper use of async/await in Web API - c#

I have an existing Business Library, which I want to expose using Web API. My existing business classes looks like
public class Business
{
public bool DoSomeBusiness()
{
//Performing long running DB operations
return true;
}
//Other methods
}
I am writing a Web API method like following code and using asyn/await for better scalability.
public class SampleController : ApiController
{
Business _business;
public ValuesController(Business business)
{
_business = business;
}
public async Task<HttpResponseMessage> Get()
{
var result= await Task.Run(() => _business.DoSomeBusiness());
return Request.CreateResponse(HttpStatusCode.OK, result);
}
}
Is this approach correct? Will I get the real benefit of the asynchronous behaviour? I don’t want to change my existing business layer implementation to make them task based.

This accomplishes nothing. If it did, ASP.NET could just run your action in a Task.Run call automatically and achieve better scalability.
You can't cheat this. You need to use async all the way down to the IOs you're making. This infects everything which is why this should be done when needed and not by default.
I'll link you two basic posts about making this choice because right now you do not understand the topic enough to make the choice:
https://stackoverflow.com/a/25087273/122718 Why does the EF 6 tutorial use asynchronous calls?
https://stackoverflow.com/a/12796711/122718 Should we switch to using async I/O by default?

Related

C# ASP.NET Core Web API: How to maintain context in the controller between calling different methods

C# ASP.NET Core Web API: general newbie question. Trying to implement basic Asynchronous Request-Reply Pattern. Basic idea is that:
Client calls POST {url}/DataProcessor/DataX with some payload
Client checks on the status of the transaction using GET {url}/DataProcessor/DataXStatus, until HTTP Status Code of 200 is returned
Client gets xActionId from the response above and then calls GET {url}/DataProcessor/GetResults/{xActionId} to retrieve results of data processing.
This is what my controller looks like: When I call DataXStatus method (after properly calling the DataX method), the _processData isn't in the right state, like it has gone out of scope after DataX method is done. What am I doing wrong? Is DataProcessorController object gone after any one method is complete? What is the right way to do this?
Thanks in advance
[Route("[controller]")]
[ApiController]
public class DataProcessorController : ControllerBase
{
private ProcessData _processData = new ProcessData() ;
[HttpPost]
[Route("DataX")]
public IActionResult DataX([FromBody] xData value)
{
_processData.CalculateResult(value);
return Accepted();
}
[HttpGet]
[Route("DataXStatus")]
public IActionResult DataXStatus()
{
if(_processData.IsRunning())
{
return Accepted();
}
return Ok(new xDataStatus(){id = _processData.GetxActionId()});
}
[HttpGet]
[Route("GetResults/{xActionId}")]
public IActionResult GetResults(string xActionId)
{
return Ok(new xDataResults(){resX = _processData.GetResults()});
}
}
Answering this on my mobile so please forgive any typos.
Basically, yes the class is re-instaintaited on every request. The api does not keep context of previous requests unless the object you are keeping track of is static, which being me to my first solution for this:
Quick and dirty option:
I would recommend you use dependency injection for this instead. That gives you the option to run your 'proccessData' class as a singleton. Which basically means it's static.
Better option:
The other more correct way is to use a known library for this. There are are tons of libraries that are built for handling async requests in web apis. I think you should use hangfire for this, it takes a few minutes to set up and also has tons of configurion options.
https://docs.hangfire.io/en/latest/getting-started/aspnet-core-applications.html
Let me know if you need any further help!

What is the simplest way to run a single background task from a controller in .NET Core?

I have an ASP.NET Core web app, with WebAPI controllers. All I am trying to do is, in some of the controllers, be able to kick off a process that would run in the background, but the controller should go ahead and return before that process is done. I don't want the consumers of the service to have to wait for this job to finish.
I have seen all of the posts about IHostedService and BackgroundService, but none of them seem to be what I want. Also, all these examples show you how to set things up, but not how to actually call it, or I am not understanding some of it.
I tried these, but when you register an IHostedService in Startup, it runs immediately at that point in time. This is not what I want. I don't want to run the task at startup, I want to be able to call it from a controller when it needs to. Also, I may have several different ones, so just registering services.AddHostedService() won't work because I might have a MyServiceB and MyServiceC, so how do I get the right one from the controller (I can't just inject IHostedService)?
Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?
You have the following options:
IHostedService classes can be long running methods that run in the background for the lifetime of your app. In order to make them to handle some sort of background task, you need to implement some sort of "global" queue system in your app for the controllers to store the data/events. This queue system can be as simple as a Singleton class with a ConcurrentQueue that you pass in to your controller, or something like an IDistributedCache or more complex external pub/sub systems. Then you can just poll the queue in your IHostedService and run certain operations based on it. Here is a microsoft example of IHostedService implementation for handling queues https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio#queued-background-tasks
Note that the Singleton class approach can cause issues in multi-server environments.
Example implementation of the Singleton approach can be like:
// Needs to be registered as a Singleton in your Startup.cs
public class BackgroundJobs {
public ConcurrentQueue<string> BackgroundTasks {get; set;} = new ConcurrentQueue<string>();
}
public class MyController : ControllerBase{
private readonly BackgroundJobs _backgroundJobs;
public MyController(BackgroundJobs backgroundJobs) {
_backgroundJobs = backgroundJobs;
}
public async Task<ActionResult> FireAndForgetEndPoint(){
_backgroundJobs.BackgroundTasks.Enqueue("SomeJobIdentifier");
}
}
public class MyBackgroundService : IHostedService {
private readonly BackgroundJobs _backgroundJobs;
public MyBackgroundService(BackgroundJobs backgroundJobs)
{
_backgroundJobs = backgroundJobs;
}
public void StartAsync(CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
if(_backgroundJobs.BackgroundTasks.TryDequeue(out var jobId))
{
// Code to do long running operation
}
Task.Delay(TimeSpan.FromSeconds(1)); // You really don't want an infinite loop here without having any sort of delays.
}
}
}
Create a method that returns a Task, pass in a IServiceProvider to that method and create a new Scope in there to make sure ASP.NET would not kill the task when the controller Action completes. Something like
IServiceProvider _serviceProvider;
public async Task<ActionResult> FireAndForgetEndPoint()
{
// Do stuff
_ = FireAndForgetOperation(_serviceProvider);
Return Ok();
}
public async Task FireAndForgetOperation(IServiceProvider serviceProvider)
{
using (var scope = _serviceProvider.CreateScope()){
await Task.Delay(1000);
//... Long running tasks
}
}
Update: Here is the Microsoft example of doing something similar: https://learn.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1#do-not-capture-services-injected-into-the-controllers-on-background-threads
As I understand from your question you want to create a fire and forget task like logging to database. In this scenario you don't have to wait for log to be inserted database. It also took much of my time to discover an easily implementable solution. Here is what I have found:
In your controller parameters, add IServiceScopeFactory. This will not effect the request body or header. After that create a scope and call your service over it.
[HttpPost]
public IActionResult MoveRecordingToStorage([FromBody] StreamingRequestModel req, [FromServices] IServiceScopeFactory serviceScopeFactory)
{
// Move record to Azure storage in the background
Task.Run(async () =>
{
try
{
using var scope = serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<ICloudStorage>();
await repository.UploadFileToAzure(req.RecordedPath, key, req.Id, req.RecordCode);
}
catch(Exception e)
{
Console.WriteLine(e);
}
});
return Ok("In progress..");
}
After posting your request, you will immediately receive In Progress.. text but your task will run in the background.
One more thing, If you don't create your task in this way and try to call database operations you will receive an error like this which means your database object is already dead and you are trying to access it;
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DBContext'.
My code is based on Repository pattern. You should not forget to inject service class in your Startup.cs
services.AddScoped<ICloudStorage, AzureCloudStorage>();
Find the detailed documentation here.
What is the simplest way to run a single background task from a controller in .NET Core?
I don't want the consumers of the service to have to wait for this job to finish.
Ultimately, everything I have seen has been a huge, convoluted mess of code for something that seems like it should be such a simple thing to do. What am I missing?
The problem is that ASP.NET is a framework for writing web services, which are applications that respond to requests. But as soon as your code says "I don't want the consumers of the service to have to wait", then you're talking about running code outside of a request (i.e., request-extrinsic code). This is why all solutions are complex: your code has to bypass/extend the framework itself in an attempt to force it to do something it wasn't designed to do.
The only proper solution for request-extrinsic code is to have a durable queue with a separate background process. Anything in-process (e.g., ConcurrentQueue with an IHostedService) will have reliability problems; in particular, those solutions will occasionally lose work.

Async IStringLocalizer - GetAllStrings

We have a custom implementation of IStringLocazlizer that Loads labels from our internal company CMS that exposes data via HTTP Rest interface.
We wanted to use NET Core built in locazlier but I do not like the GetAllStrings Sync method that will have to Block on Tasks to perfrom HTTP Call.
We have a cache obvioulsy but I do think that it does not seem right.
Any thoughts on that?
Example:
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
{
Task<CmsLabelsModel> task = pipeline.SendAsync(new GetLabelsModelRequest(ResourceName));
CmsLabelsModel result = task.GetAwaiter().GetResult(); //Yuk Yuk
return result.LabelModels.Select(pair => new LocalizedString(pair.Key, pair.Value.Value));
}

Calling an async WCF Service while being impersonated

I have a WCF Service running on a Server, which is configured to accept Kerberos authentication.
Kerberos works fine and the WCF Service therefore knows, which user is connecting to him.
The Service offers everything as Async Methods. Like this here (just an example for clearity).
public ExampleService : IExampleService {
public Task<string> GetUsernameAsync() {
return await Task.Run(() => System.Threading.Thread.CurrentPrincipal.Name);
}
}
On the Client side I have an Controller (it's an MVC-page, but that does not matter), which calls the methods asyncronously.
public ExampleController {
public async Task<ActionResult> Index() {
using(var serviceClient = ServiceFactory.GetServiceClient())
using(Security.Impersonation.Impersonate())
{
var data = await serviceClient.GetUsernameAsync();
return View(data);
}
}
}
The impersonation works fine, as long as I do not use await.
Since Task<> does not flow the impersonated identity, I'd like to know if there is some possibility, to change the executing user of the Task or to do anything else to make the impersonation work in this use-case.
I tried a custom awaiter (as it can be done with Culture in that Case), but that does not work at all (Well it just does not impersonate as well).
Okay - after some more in depth research I finally found the solution how to flow impersonated windows identities across asynchronous tasks.
The solution is machine-wide and will be set for all (in this case) 64bit ASP.NET 4.5 applications.
Find the aspnet.config file in C:\Windows\Microsoft.Net\Framework64\v4.0.30319 (probably this will apply for later versions, too) and change the value of legacyImpersonationPolicy to false
<legacyImpersonationPolicy enabled="false"/>
Make sure to restart IIS (or reboot the machine).
This will then make Impersonation flowing, as long as you use managed methods for the impersonation. In my case I impersonate similar to this, which works fine:
class Impersonation : IDisposable
{
public static Impersonation Impersonate()
{
return new Impersonation();
}
private WindowsImpersonationContext ImpersonationContext { get; set; }
private Impersonation()
{
var currentIdentity = System.Threading.Thread.CurrentPrincipal.Identity as WindowsIdentity;
if (currentIdentity != null && currentIdentity.IsAuthenticated)
{
ImpersonationContext = currentIdentity.Impersonate();
return;
}
throw new SecurityException("Could not impersonate user identity");
}
public void Dispose()
{
if(ImpersonationContext != null)
ImpersonationContext.Dispose();
}
}
}
The aspnet.config setting (btw. it did not work to set it in the web.config file) is explained here: http://msdn.microsoft.com/en-us/library/ms229296(v=vs.110).aspx (it basically says, if this is true, we do it the .NET 1.1 way)
You can check, if the windows identity is flowed or not by using this method:
System.Security.SecurityContext.IsWindowsIdentityFlowSuppressed()
I disagree with your QUESTION.
The problem isn't your await. But your Task.Run. There should really not be a await Task.Run on you ASP.Net code. The effect of it is an unnecessary thread switch. Since you don't have STA threads on ASP.Net, there is no need for this and it just slows down your code.
If you stick to real threadless Tasks you shouldn't have any problems, as you will stay in a single thread. Unless your application server has a very limited number of clients and a huge amount of CPU bound operations, multi-threading is bad for scaling, as a single user can quickly fill up the schedule of your server.
You should really be using Task.FromResult, or TaskCompletionSource.Task to ensure that you remain single-thread. Which co-incidentally will fix your problem with [ThreadLocal] properties.
TL:DR
Don't use Task.Run on your server-side. Use Task.FromResult so you only have one thread.
EDIT:Response
Which thread? On the client side you are still going to use await. I never said don't use await. I said DON'T ever use await directly with Task.Run (except on the UI thread). I didn't say you should BLOCK a thread. As your thread should be doing WORK to produce the result that you pass into the Task.FromResult. BLOCKING means that you thread does nothing, whilst consuming resources (namely memory). Heck, there isn't even a need to
Server side should use this pattern:
public ExampleService : IExampleService
{
public Task<string> GetUsernameAsync()
{
var name = System.Threading.Thread.CurrentPrincipal.Name;
return Task.FromResult(name);
}
}
client should remain
public ExampleController
{
public async Task<ActionResult> Index()
{
using(var serviceClient = ServiceFactory.GetServiceClient())
using(Security.Impersonation.Impersonate())
{
var data = await serviceClient.GetUsernameAsync();
return View(data);
}
}
}
and in the case where your ServiceClient resolves locally, everything runs synchronously (faster and with less resources). The point here is that you are only applying the Task async pattern for the There is no thread style of async. Task.Run is the concurrency style of async, and should only be used when you need to use another thread (either because you are CPU bound, or THIS thread NEEDS to be used for something else).
Since I am in charge of the WCF interfaces here is one solution, which works (but which I do not like, since it is more or less code duplication):
[ServiceContract]
interface IExampleService {
[OperationContract]
string GetUsername();
}
interface IExampleServiceAsync {
Task<string> GetUserNameAsync();
}
class ExampleService : IExampleService {
public string GetUsername() {
return System.Threading.Thread.CurrentPrincipal.Name;
}
}
class ExpampleServiceClient : ServiceClient<IExampleService>, IExampleServiceAsync {
public Task<string> GetUsernameAsync() {
return Task.Run(() => GetUsername());
}
private string GetUsername() {
using(Security.Impersonation.Impersonate())
{
return base.Proxy.GetUsername();
}
}
}
I have to say this is a workaround - not a solution - and it changes the Interfaces on Server-Side (to non-Async interfaces only), but at least it is working.
One plus for this solution - you can implement the impersonation as a behavior pattern on top of the ExampleServiceClient.

Changing Synchronous operations into Asynchronous in Business Service & Repository Layers (ASP.NET MVC 2)

I am working on a Travel related website and here is my high level architecture:
ASP.NET MVC 2 (Presentation Layer)
Services Layer
Repository Layer communicating with external J2EE Services
Currently all of my layers support ONLY Synchronous communication like this:
//Pricing Controller...
public ActionResult GetRates()
{
//Contruct Pricing Request
//PricingRequest request = Contruct Pricing Request...;
PricingResponse response = _pricingService.GetComponentPricing(request);
//....
//...
return View(response);
}
//Pricing Service...
public PricingResponse GetComponentPricing(PricingRequest pricingRequest)
{
//Do Something...
PricingResponseDomainObject responseDomainObject = _pricingRepository.GetComponentPricing(pricingRequest.ConvertToPricingRequestDomainObject());
//Apply Business Rules and perform some action on response
//Convert DomainObjectResponse into ServiceObjectResponse...
return response(PricingResponse);
}
//Pricing Repository...
public PricingResponseDomainObject GetComponentPricing(PricingRequestDomainObject pricingRequest)
{
//Do Something...
//Call J2EE Service and Get Response
//Return Response
return response(PricingResponseDomainObject);
}
Now I have a need to change my Pricing Controller to AsyncController and convert public ActionResult GetRates() into Asynchronous Action.
What changes do I need to do in my Service & Repository layers in order to support Asynchronous operations? Do I have to re-write them completely?
Edit: Need for changing Controller into Asynchronous
For a particular Search scenario, I need to call the J2EE service 1 to 5 times (independent operations), consolidate all responses into a single response and hand it over to the Controller to be able to present it to user.
No, you likely will not have to do anything. In fact, converting them to async themselves would make your code a lot more complicated. The idea behind an async controller is that the controller action returns when it's done processing, and since those methods will still be syncronous that works out just fine. You do your work, and return when you're finished.

Categories

Resources