title: Best approach to execute a long running task in asp.net mvc5
body: <p>I am working on an asp.net mvc5 web application + Entity framework 6.0 deployed inside IIS 7.0. currently i have a NetworkScanning server which I implemented as a normal action method , that  can be started in 2 ways :-</p>
  
  <p>1.based on a schedule defined inside the global.asax:-</p>
  
  <pre><code>static void ScheduleTaskTrigger()
          {
              HttpRuntime.Cache.Add("ScheduledTaskTrigger",
                                    string.Empty,
                                    null,
                                    Cache.NoAbsoluteExpiration,
                                    TimeSpan.FromMinutes(Double.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["ScanInMinutes"])), // as defined inside the web.config
                                    CacheItemPriority.NotRemovable,
                                    new CacheItemRemovedCallback(PerformScheduledTasks));
          }
  
          static void PerformScheduledTasks(string key, Object value, CacheItemRemovedReason reason)
          {
              //Your TODO
              HomeController h = new HomeController();
              var c = h.ScanNetwork("***", "allscan");
              ScheduleTaskTrigger();
          }
  </code></pre>
  
  <p>2.or manually calling the action method from the user browser.</p>
  
  <p>now the action method looks as follow (i removed a lot of code as the idea is to give a hint on what i am doing inside the action method):-</p>
  
  <pre><code>public async Task&lt;ActionResult&gt; ScanNetwork(string token, string FQDN) 
          {
              string TToken = System.Web.Configuration.WebConfigurationManager.AppSettings["TToken"];//get the T token from the web.config, this should be encrypted
              var cccc = Request == null ? System.Web.Configuration.WebConfigurationManager.AppSettings["TIP"] : Request.UserHostAddress;
              if (token != TToken || cccc != System.Web.Configuration.WebConfigurationManager.AppSettings["TIP"]) 
              {
  
  
                  if (FQDN != "allscan")
                  { return Json(new { status = "fail", message = "Authintication failed." }, JsonRequestBehavior.AllowGet); }
                  return new HttpStatusCodeResult(403, "request failed");
              }
  
              try
              {
  
                scaninfo = await repository.populateScanInfo(false); // get the info for all the servers from the database
  
  
  
                  using (WebClient wc = new WebClient()) 
                  {
                      string url = currentURL + "resources?AUTHTOKEN=" + pmtoken;
                      var json = await wc.DownloadStringTaskAsync(url);
                      resourcesinfo = JsonConvert.DeserializeObject&lt;ResourcesInfo&gt;(json);
                  }
  
  
                  foreach (var c in scaninfo) //loop through all the hypervisot server/s
                  {
  
  
                      if (passwordmanagerResource.Count() == 0) // if there is not any record for the resource on the password manager
                      {
  
  
                         await repository.Save();
                          continue;
  
                      }
                      else if (passwordmanagerResource.Count() &gt; 1) // if more than on record is defined for the same resource on PM
                      {
  
  
                          await repository.Save();
                          continue;
  
                      }
                      else
                      {
  
  
  
                          using (WebClient wc = new WebClient()) // call the PM API to get the account id 
                          {
                              string url = currentURL + "resources/" + passwordmanagerResourceID + "/accounts?AUTHTOKEN=" + pmtoken;
                              var json = await wc.DownloadStringTaskAsync(url);
  
                              resourceAccountListInfo = JsonConvert.DeserializeObject&lt;ResourceAccountListInfo&gt;(json);
                          }
  
                          using (WebClient wc = new WebClient()) // call the PM API to get the password for the account id under the related resource
                          {
                              string url = currentURL + "resources/" + passwordmanagerResourceID + "/accounts/" + passwordmanagerAccountID + "/password?AUTHTOKEN=" + pmtoken;
                              var json = await wc.DownloadStringTaskAsync(url);
                              resourceAccountPasswordListInfo = JsonConvert.DeserializeObject&lt;ResourceAccountPasswordInfo&gt;(json);
                          }
  
                          var shell = PowerShell.Create();
                          var shell2 = PowerShell.Create();
                          var shell3 = PowerShell.Create();
  
                          //Powercli script to get the hypervisot info
                          string PsCmd = "add-pssnapin VMware.VimAutomation.Core; $vCenterServer = '" + vCenterName + "';$vCenterAdmin = '" + vCenterUsername + "' ;$vCenterPassword = '" + vCenterPassword + "';" + System.Environment.NewLine;
  
  
  
                          PsCmd = PsCmd + "$VIServer = Connect-VIServer -Server $vCenterServer -User $vCenterAdmin -Password $vCenterPassword;" + System.Environment.NewLine;
  
  
  
                          PsCmd = PsCmd + "Get-VMHost " + System.Environment.NewLine;
  
                          string PsCmd2 = "add-pssnapin VMware.VimAutomation.Core; $vCenterServer = '" + vCenterName + "';$vCenterAdmin = '" + vCenterUsername + "' ;$vCenterPassword = '" + vCenterPassword + "';" + System.Environment.NewLine;
  
  
  
                          PsCmd2 = PsCmd2 + "$VIServer = Connect-VIServer -Server $vCenterServer -User $vCenterAdmin -Password $vCenterPassword;" + System.Environment.NewLine;
  
  
  
                          PsCmd2 = PsCmd2 + " Get-VMHost " + vCenterName + "| Get-VMHostNetworkAdapter -VMKernel" + System.Environment.NewLine;
  
  
  
                          shell.Commands.AddScript(PsCmd);
                          shell2.Commands.AddScript(PsCmd2);
                          dynamic results = shell.Invoke(); // execute the first powercli script
                          dynamic results2 = shell2.Invoke();//execute the second powercli script
  
  
                          if (results != null &amp;&amp; results.Count &gt; 0 &amp;&amp; results[0].BaseObject != null) // the powercli executed successfully
                          {
                              // call the service desk API to update the hypervisor info
                              var builder = new StringBuilder();
  
                              XmlDocument doc = new XmlDocument();
                              using (var client = new WebClient())
                              {
  
                                  var query = HttpUtility.ParseQueryString(string.Empty);
  
  
                                //code goes here
  
                                  string xml = await client.DownloadStringTaskAsync(url.ToString());
  
                                  doc.LoadXml(xml);
                                  status = doc.SelectSingleNode("/operation/operationstatus").InnerText;
                                  message = doc.SelectSingleNode("/operation/message").InnerText;
  
  
                              }
  
                          else//if the powershell script return zero result..
                          {
  
                              c.TServer.ScanResult = "Scan return zero result";
                              scan.Description = scan.Description + "&lt;span style='color:red'&gt;" + c.TServer.ScanResult + "&lt;/span&gt;&lt;br/&gt;";
                              await repository.Save();
                              continue;
                          }
                          if (FQDN == "allscan")
                          {
  
                             //code goes here
              }
              catch (WebException ex)
              {
                  errormessage = "Password manager or manage engine can not be accessed";
                  errorstatus = "fail";
  
              }
              catch (Exception e)
              {
                  errormessage = "scan can not be completed. Message" + e.InnerException;
                  errorstatus = "fail";
  
              }
              scan.EndDate = System.DateTime.Now;
  
  
                  using (MailMessage mail = new MailMessage(from, "*****"))
                  {
                      mail.Subject = "scan report generated";
                      //  mail.Body = emailbody;
  
                      mail.IsBodyHtml = true;
                      System.Text.StringBuilder mailBody = new System.Text.StringBuilder();
                      mailBody.AppendLine("&lt;span style='font-family:Segoe UI'&gt;Hi, &lt;br/&gt;");
                      mailBody.AppendLine(scan.Description);
                      mailBody.AppendLine("&lt;br/&gt;&lt;br/&gt;&lt;div style='color:#f99406;font-weight:bold'&gt;T scanning Management &lt;/div&gt; &lt;br/&gt; &lt;div style='color:#f99406;font-weight:bold'&gt;Best Regards&lt;/div&gt;&lt;/span&gt;");
  
                      SmtpClient smtp = new SmtpClient();
                      smtp.Host = System.Web.Configuration.WebConfigurationManager.AppSettings["smtpIP"];
                      smtp.EnableSsl = true;
                      mail.Body = mailBody.ToString();
  
                      smtp.UseDefaultCredentials = false;
  
                      smtp.Port = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["smtpPort"]);
                      S
                      smtp.Send(mail);
                  }
              }
              return Json(new { status = errorstatus, message = errormessage }, JsonRequestBehavior.AllowGet);
  
  
  
          }
  </code></pre>
  
  <p>now as shown on the above action method it contain many operations such as ; retrieve objects from DB , save DB changes, call web clients on third party applications, run powercli scripts , etc.. now i deployed the application on IIS and seems it is working fine, either when it run manually by users or based on the schedule time. but from my own reading is that running long running tasks such as the above action method is considered risky, and i need to use different approach . so can anyone adivce why the above approach is considered risky and how i can improve it ?
  second question. now i read that Quartz.NET is an approach to follow, but not sure if i can still call Quartz.NET method from a web browser &amp; not sure if i can call webclient, execute powercli scripts, etc inside Quartz.NET ?</p>
  
  <p>Thanks</p>