Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC: Exception rendering view in new thread

I want to create a process in my ASP.NET application that I trigger manually and will send a bunch of emails to my users. Since this process takes a while I am creating a new thread to send these messages and prevent having timeouts in my web app. (I know this is error prone in case the app pool is recycled or there's an unhandled exception in the app, but that's another subject).

For that I'm doing something like this:

public ActionResult SendMessages()
{
  Task.Factory.StartNew(() => { SendMessagesLongRunningProcess(); });
  return View();
}

inside the long running process I'm trying to render the view for the HTML email and send it. Like this:

private void SendMessagesLongRunningProcess()
{
  var users = GetUsers();
  foreach (var user in users)
  {
    string message = RenderView("EmailMessage", user);
    SendEmail(user.email, message);
  }
}

Now, I know that my RenderView method works just fine since I use it to render email views in other places. The problem is when I try to execute it in a new thread as I'm doing here. This is my RenderView method:

    public string RenderView(string viewName, object model)
    {
        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindView(ControllerContext, viewName, null);
            var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            return sw.GetStringBuilder().ToString();
        }
    }

The error I'm getting is:

Value does not fall within the expected range.

When the viewResult.View.Render method is called.

I'm guessing this has to do with the fact that the controller context is no longer valid in the new thread, but I'm not sure.

So, what is the best way to approach this? What alternatives do I have?

Thanks

like image 343
willvv Avatar asked Aug 16 '12 22:08

willvv


1 Answers

I'm guessing this has to do with the fact that the controller context is no longer valid in the new thread, but I'm not sure.

Precisely.

So, what is the best way to approach this?

Send emails from another process, not your ASP.NET MVC application.

What alternatives do I have?

One possibility is to use RazorEngine:

private void SendMessagesLongRunningProcess()
{
    var users = GetUsers();
    foreach (var user in users)
    {
        string view = Server.MapPath("~/Views/MailTemplates/SomeTemplate.cshtml");
        string template = System.IO.File.ReadAllText(view);
        string message = Razor.Parse(template, user);
        SendEmail(user.email, message);
    }
}

You might also checkout MvcMailer and send the email asynchronously (take a look at the Send Email Asynchronously section of the step-by-step-guide).

like image 83
Darin Dimitrov Avatar answered Oct 31 '22 19:10

Darin Dimitrov