Search code examples
c#asp.net-mvcasynchronousiisserver

.NET MVC: perform heavy task on server then email user when complete so that user can leave site instead of wait?


The scenario: A button allows a user to merge a large number of PDF documents to download as a single PDF. Currently the action of getting all the PDF's and merging can take upwards of a minute or more while the user has to wait for the download to start.

My goal is to allow the user to leave if they want. The solution I thought of would be to merge the documents in the background on the server and then email a link to the user when it's completed but I'm open to other solutions.

The thing that I don't understand is how to perform the merging asynchronously in the background. Using .NET, MVC 5, DevExpress.

The code looks kinda like:

$.ajax({
   type: "POST",
   url: '@Url.Action("ExportMergedDocuments_PersonnelId", "Personnel", new { personnelId = Model.Id })',
}).done(function(data) {
   window.location.href = '@Url.RouteUrl(new { Controller = "Personnel", Action = "Download"})/?file=' + data.fileName; }
});
[HttpPost]
public JsonResult ExportMergedDocuments_PersonnelId(int PersonnelId)
{
   var allDocuments = new DataSet();
   allDocuments.Merge(GetDocuments((int)PersonnelId, ".....1").Tables[0]);
   allDocuments.Merge(GetDocuments((int)PersonnelId, ".....2").Tables[0]);

   string fileName = $"merged__{DateTime.Now.ToString("yyyyMMddHHmm")}.pdf";
   if (MergePdfSet(fileName, allDocuments))
      return Json(new { fileName });
   // else error msg
}

Download the file:

[HttpGet]
public ActionResult Download(string file)
{
   return File(..fullpath.., "application/pdf", file);
}

Merging Pdfs:

public bool MergePdfSet(string fileName, DataSet allDocuments)
{
   bool merged = false;
   string fullPath = Path.Combine(Server.MapPath("~/App_Data/temp/"), fileName);
   using (var pdfDocumentProcessor = new PdfDocumentProcessor())
   {
      pdfDocumentProcessor.CreateEmptyDocument(fullPath);
      foreach (DataRow row in allDocuments.Tables[0].Rows)
      {
         var documentId = (int)row["DocumentID"];
         var fetchedDocument = GetFile(documentId);
         pdfDocumentProcessor.AppendDocument(fetchedDocument);
         merged = true;
      }
   }
   return merged;
}

Solution

  • Two option comes to mind:

    1. Create a new thread and run the code there but don't await it.
    2. Use Hangfire (https://www.hangfire.io/), you can easy enqueue a job.