ASP.NET MVC Razor: Cómo renderizar un HTML de Razor Partial View dentro de la acción del controlador

Se sabe cómo generar un HTML de una vista parcial dada en el motor de vista ASP.NET.

Pero si esta funcionalidad se usa en la vista parcial de la maquinilla de afeitar, no funciona, ya que la excepción dice que la vista parcial no se deriva de “UserControl”.

¿Cómo arreglar la representación para apoyar la vista parcial de la maquinilla de afeitar?

Necesito esto porque genero correos electrónicos desde estas vistas parciales …

ACTUALIZAR:

Código que falla (@mcl):

public string RenderPartialToString(string controlName, object viewData) { ViewPage viewPage = new ViewPage() { ViewContext = new ViewContext() }; viewPage.Url = this.GetUrlHelper(); string fullControlName = "~/Views/Email/" + controlName + ".ascx"; viewPage.ViewData = new ViewDataDictionary(viewData); viewPage.Controls.Add(viewPage.LoadControl(fullControlName)); StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb)) { using (HtmlTextWriter tw = new HtmlTextWriter(sw)) { viewPage.RenderControl(tw); } } return sb.ToString(); } 

 @Html.Partial("nameOfPartial", Model) 

Actualizar

 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(); } } 

Aunque ya se han dado las respuestas adecuadas, me gustaría proponer una solución menos detallada, que se puede utilizar sin los métodos de ayuda disponibles en una clase de controlador MVC. Utilizando una biblioteca de terceros llamada “RazorEngine” puede usar .Net file IO para obtener el contenido del archivo de afeitar y llamar

 string html = Razor.Parse(razorViewContentString, modelObject); 

Obtenga la biblioteca de terceros aquí .

También puede usar la RenderView Controller extension del RenderView Controller extension desde aquí ( fuente )

y úsalo así:

 public ActionResult Do() { var html = this.RenderView("index", theModel); ... } 

Funciona para razors y web-forms viewengines

Vi que alguien se preguntaba cómo hacerlo para otro controlador.

En mi caso, tenía todas mis plantillas de correo electrónico en la carpeta Vistas / Correo electrónico, pero podría modificar esto para pasar el controlador en el que tiene vistas asociadas.

 public static string RenderViewToString(Controller controller, string viewName, object model) { var oldController = controller.RouteData.Values["controller"].ToString(); if (controller.GetType() != typeof(EmailController)) controller.RouteData.Values["controller"] = "Email"; var oldModel = controller.ViewData.Model; controller.ViewData.Model = model; try { using (var sw = new StringWriter()) { var viewResult = ViewEngines.Engines.FindView(controller.ControllerContext, viewName, null); var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw); viewResult.View.Render(viewContext, sw); //Cleanup controller.ViewData.Model = oldModel; controller.RouteData.Values["controller"] = oldController; return sw.GetStringBuilder().ToString(); } } catch (Exception ex) { Elmah.ErrorSignal.FromCurrentContext().Raise(ex); throw ex; } } 

Esencialmente, lo que hace es tomar un controlador, como AccountController, y modificarlo para que piense que es un controlador de correo electrónico para que el código se vea en la carpeta Views/Email . Es necesario hacer esto porque el método FindView no toma una ruta recta como parámetro, quiere un ControllerContext .

Una vez hecho el renderizado de la cadena, devuelve el AccountController a su estado inicial para ser utilizado por el objeto Response.

gran código; pequeña pista: si a veces tiene que omitir más datos y no solo el modelo de vista …

  if (model is ViewDataDictionary) { controller.ViewData = model as ViewDataDictionary; } else { controller.ViewData.Model = model; } 

Préstamo @jgauffin respuesta como una extensión HtmlHelper:

 public static class HtmlHelperExtensions { public static MvcHtmlString RenderPartialViewToString( this HtmlHelper html, ControllerContext controllerContext, ViewDataDictionary viewData, TempDataDictionary tempData, string viewName, object model) { viewData.Model = model; string result = String.Empty; 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); result = sw.GetStringBuilder().ToString(); } return MvcHtmlString.Create(result); } } 

Uso en una vista de afeitar:

 Html.RenderPartialViewToString(ViewContext, ViewData, TempData, "Search", Model)