I need to generate html (for the body of an email message) specific to a Customer
object
I thought of making a View
that gets a Customer
object and renders the appropriate text.
Is there a way to call a view and get the rendered output without associating it to a controller action?
Ideally in pseydocode I would do something like this
customer = new Customer();
view = new GetCustomerEmailBodyView(customer);
string htmlBody = view.SomeFunctionToRenderViewAndGetOutput()
I have found a solution to get the HTML of a view here that has an action returning a StringResult (inherits from ViewResult) instead of ActionResult which exposes an Html
property.
However I still have to make a custom action to call it, and I don't like the fact that it depends on the ControllerContext making it hard to test it.
Is what I am requesting against the MVC principals? How should my code be structured for this scenario?
Original code from here
protected string RenderPartialViewToString(string viewName, object model) {
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.RouteData.GetRequiredString("action");
ViewData.Model = model;
using (StringWriter sw = new StringWriter()) {
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
Are you saying that you want some program to be able to leverage a View without being in a controller context at all, or are you saying that you want to be able to render a view into a string from within a controller, without calling some other controller?
For the former, I can't be of much assistance, but for the latter, we have this method in the base controller type that we inherit with all our other controllers:
/// <summary>
/// Generates a string based on the given PartialViewResult.
/// </summary>
/// <param name="partialViewResult"></param>
/// <returns></returns>
protected internal string RenderPartialViewToString(ViewResultBase partialViewResult)
{
Require.ThatArgument(partialViewResult != null);
var context = ControllerContext;
Require.That(context != null);
using (var sw = new StringWriter())
{
if (string.IsNullOrEmpty(partialViewResult.ViewName))
{
partialViewResult.ViewName = context.RouteData.GetRequiredString("action");
}
ViewEngineResult result;
if (partialViewResult.View == null)
{
result = partialViewResult.ViewEngineCollection.FindPartialView(context, partialViewResult.ViewName);
Require.That(result.View != null,
() => new InvalidOperationException(
"Unable to find view. Searched in: " +
string.Join(",", result.SearchedLocations)));
partialViewResult.View = result.View;
}
var view = partialViewResult.View;
var viewContext = new ViewContext(context, view, partialViewResult.ViewData,
partialViewResult.TempData, sw);
view.Render(viewContext, sw);
return sw.ToString();
}
}
Usage:
public ActionResult MyAction(...)
{
var myModel = GetMyModel(...);
string viewString = RenderPartialViewToString(PartialView("MyView", myModel));
// do something with the string
return someAction;
}
We actually use this in an event-based AJAX model, where most of our actions actually just return an AJAX-encoded list of client-side events, and some of those client-side events may be to update a particular DOM element with the string produces by rendering this partial view.