The following code works fine for rendering a Razor View to a string:
///
/// url: /api/createHtml
///
public ActionResult CreateHtml()
{
// Heavy calculations
MyModel myModel = new MyModel();
myModel.Feature1 = ...;
myModel.Feature2 = ...;
myModel.Feature3 = ...;
ViewData.Model = myModel;
using (var stringWriter = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, "MyView");
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, stringWriter);
viewResult.View.Render(viewContext, stringWriter);
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
string html = stringWriter.GetStringBuilder().ToString();
byte[] htmlBytes = Encoding.ASCII.GetBytes(html);
System.IO.File.WriteAllBytes(Server.MapPath("~/temp/foo.html"), htmlBytes);
}
return JSON(new
{
error = false,
message = "Your view is available at temp/foo.html"
});
}
The above code runs synchronously, meaning that an AJAX request to /api/createHtml/
will finish with the temp/foo.html
file created.
I want to do this asynchronously: meaning that the AJAX request returns fast to the user with a message like: "Your view WILL BE available at temp/foo.html". And then the user must go check if the file is ready (by simply polling to the temp directory [or using other method, not important in this question])
So, when I try the same code within a Task
, it doesn't work:
///
/// url: /api/createHtml
///
public ActionResult CreateHtml()
{
new Task(() =>
{
// Heavy calculations
MyModel myModel = new MyModel();
myModel.Feature1 = ...;
myModel.Feature2 = ...;
myModel.Feature3 = ...;
ViewData.Model = myModel;
using (var stringWriter = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, "MyView");
var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, stringWriter);
viewResult.View.Render(viewContext, stringWriter); // <--- Problem
viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
string html = stringWriter.GetStringBuilder().ToString();
byte[] htmlBytes = Encoding.ASCII.GetBytes(html);
System.IO.File.WriteAllBytes(Server.MapPath("~/temp/foo.html"), htmlBytes);
}
}).Start();
return JSON(new
{
error = false,
message = "Your view _WILL BE_ available at temp/foo.html"
});
}
It doesn't work because it throws an Exception at viewResult.View.Render(...)
Value does not fall within the expected range.
It seems that the viewContext
passed to viewResult.View.Render(...)
is no longer valid in the new thread, as shown here: ASP.NET MVC: Exception rendering view in new thread
Is there a workaround for rendering a view within a Task
?
I know I could use "RazorEngine", a free library that renders razor views without all the controller mumbo jumbo, but I'd prefer to use native code, for reutilization of the code.
POST EDITED:
The few answers thought that I wanted to use "await async". I don't. I don't want to wait for the task to finish.