Skip to main content

ASP.NET MVC AsyncController Helper

The AsyncController is a really nice feature introduced in ASP.NET MVC 2. It allows you to process your actions  asynchronously increasing the throughput of your web server.
Although implementing it is not that hard, I’m not a big fan of the current syntax(The new async api in ASP.NET MVC 4 looks promising).
In the meanwhile I have written a small helper class to simplify the usage of the AsyncController.
public static class AsyncManagerExtensions
   {
       public static void RegisterTask<T>(this AsyncManager asyncManager, Task<T> task, Func<T, object> func)
       {
           asyncManager.OutstandingOperations.Increment();
           task.ContinueWith(task2 =>
           {
               object propertyObject = null;

               if (!task2.IsFaulted)
               {
                   //invoke the provided function with the
                   //result of running the task
                   propertyObject = func(task2.Result);
               }
               else
               {
                   propertyObject = func(default(T));
               }


               //use reflection to set asyncManager.Parameters
               //for the returned object's fields and properties
               var ty = propertyObject.GetType();

               foreach (var f in ty.GetFields())
               {
                   asyncManager.Parameters[f.Name] = f.GetValue(propertyObject);
               }

               foreach (var p in ty.GetProperties())
               {
                   var v = p.GetGetMethod().Invoke(propertyObject, null);
                   asyncManager.Parameters[p.Name] = v;
               }

               asyncManager.OutstandingOperations.Decrement();
           });
       }
   }

Using it is simple. I create a controller that inherits from AsyncController:

public class PrintController : AsyncController
{
    private readonly IReportService _reportService;
        
    public PrintController(IReportService reportService)
    {
        _reportService = reportService;
    }
}


I add a method that returns the Task I want to execute and and the Action Controller method that will be called by the View(in this case through /Print/Pdf) . In this Action Controller method I call the RegisterTask<T> extension method and pass 2 properties:

  • The method that needs to be executed 
  • An expression that takes the output of the task and maps it to the parameters of the Completed method(based on the parameter name).
public void PdfAsync(string reportName)
{
 AsyncManager.RegisterTask<byte[]>(RenderReport(reportName)), renderReportResult=> new { data = renderReportResult, reportName = reportName });
}

private Task<byte[]> RenderReport(string reportName)
{
 return  Task.Factory.StartNew(()=>_reportService.RenderReport(reportName));
}

public ActionResult PdfCompleted(byte[] data, string reportName)
{
 var fileName = reportName + ".pdf";
 var fileContentResult = File(data, "application/pdf", fileName);
 Response.AppendHeader("Content-Disposition", string.Format("inline; filename={0}", fileName));
 return fileContentResult;
}

Popular posts from this blog

Podman– Command execution failed with exit code 125

After updating WSL on one of the developer machines, Podman failed to work. When we took a look through Podman Desktop, we noticed that Podman had stopped running and returned the following error message: Error: Command execution failed with exit code 125 Here are the steps we tried to fix the issue: We started by running podman info to get some extra details on what could be wrong: >podman info OS: windows/amd64 provider: wsl version: 5.3.1 Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM Error: unable to connect to Podman socket: failed to connect: dial tcp 127.0.0.1:2655: connectex: No connection could be made because the target machine actively refused it. That makes sense as the podman VM was not running. Let’s check the VM: >podman machine list NAME         ...

Azure DevOps/ GitHub emoji

I’m really bad at remembering emoji’s. So here is cheat sheet with all emoji’s that can be used in tools that support the github emoji markdown markup: All credits go to rcaviers who created this list.

VS Code Planning mode

After the introduction of Plan mode in Visual Studio , it now also found its way into VS Code. Planning mode, or as I like to call it 'Hannibal mode', extends GitHub Copilot's Agent Mode capabilities to handle larger, multi-step coding tasks with a structured approach. Instead of jumping straight into code generation, Planning mode creates a detailed execution plan. If you want more details, have a look at my previous post . Putting plan mode into action VS Code takes a different approach compared to Visual Studio when using plan mode. Instead of a configuration setting that you can activate but have limited control over, planning is available as a separate chat mode/agent: I like this approach better than how Visual Studio does it as you have explicit control when plan mode is activated. Instead of immediately diving into execution, the plan agent creates a plan and asks some follow up questions: You can further edit the plan by clicking on ‘Open in Editor’: ...