Render MVC PartialView en respuesta SignalR

Me gustaría representar un PartialView a una cadena HTML para poder devolverlo a una solicitud ajax de SignalR.

Algo como:

SignalR Hub (mySignalHub.cs)

public class mySignalRHub: Hub
{
    public string getTableHTML()
    {
        return PartialView("_MyTablePartialView", GetDataItems()) // *How is it possible to do this*
    }
}

Razor PartialView (_MyTablePartialView.cshtml)

@model IEnumerable<DataItem>

<table>
    <tbody>
        @foreach (var dataItem in Model)
        {
        <tr>
            <td>@dataItem.Value1</td>
            <td>@dataItem.Value2</td>
        </tr>
        }
    </tbody>
</table>

HTML (MySignalRWebPage.html)

<Script>
    ...      
    //Get HTML from SignalR function call
    var tableHtml = $.connection.mySignalRHub.getTableHTML();

    //Inject into div
    $('#tableContainer).html(tableHtml);
</Script>

<div id="tableContainer"></div>

Mi problema es que parece que no puedo reproducir un PartialView fuera de un controlador. ¿Es incluso posible renderizar una vista parcial fuera de un controlador? Sería muy bueno poder aprovechar las increíbles capacidades de generación de HTML que vienen con Razor.

¿Estoy yendo todo esto mal? ¿Hay otra manera?

 Francesco Abbruzzese02 ago. 2012 16:17
No es una forma conveniente de proceder. Es mejor devolver solo los datos de kson y luego distinguir el tempate del lado del cliente, que ya está en la página. El tempate, a su vez, se puede representar con una vista parcial
 Samuel Fleming25 jul. 2012 22:14
También te interesa ... ¿Ideas? ¿Puedes hacer parciales fuera de los controladores?

Respuestas a la pregunta(6)

r comohttp://razorengine.codeplex.com/ ? No puede usarlo para analizar vistas parciales, pero puede usarlo para analizar plantillas de maquinillas de afeitar, que son casi similares a las vistas parciales.

 James06 ago. 2012 20:24
Interesante, gracias. Ver comentario en la publicación de Paolo a continuación. Puedo probar más, pero parece que hay algunos problemas potenciales con ese proyecto.

RazorEngineHost yRazorTemplateEngine. encontré estoBuen articulo Eso podría ser lo que estás buscando. Se trata de alojar Razor fuera de ASP.NET (MVC).

también superé esto y terminé con una función que devuelve el PartialView renderizado dado un nombre de controlador, una ruta a la vista y el modelo.

Tiene en cuenta el hecho de que no tiene un controlador y, por lo tanto, ninguno de los estados habituales provienen de un evento SignalR.

public static string RenderPartialView(string controllerName, string partialView, object model)
{
    var context = new HttpContextWrapper(System.Web.HttpContext.Current) as HttpContextBase;

    var routes = new System.Web.Routing.RouteData();
    routes.Values.Add("controller", controllerName);

    var requestContext = new RequestContext(context, routes);

    string requiredString = requestContext.RouteData.GetRequiredString("controller");
    var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
    var controller = controllerFactory.CreateController(requestContext, requiredString) as ControllerBase;

    controller.ControllerContext = new ControllerContext(context, routes, controller);      

    var ViewData = new ViewDataDictionary();

    var TempData = new TempDataDictionary();

    ViewData.Model = model;

    using (var sw = new StringWriter())
    {
        var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialView);
        var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, ViewData, TempData, sw);

        viewResult.View.Render(viewContext, sw);
        return sw.GetStringBuilder().ToString();
    }
}

Usted lo llamaría con algo similar a:

RenderPartialView("MyController", "~/Views/MyController/_partialView.cshtml", model);
 Shahid Iqbal02 abr. 2018 16:31
Response is not available in this context. Estoy recibiendo esta excepción envar viewResult=ViewEngines.Engines.FindView(controller.ControllerContext,viewName,masterName). alguna ayuda.?
Solución de preguntas

esto es lo que uso en Controladores para ajax, lo modifiqué un poco para que pueda llamarse desde el método en lugar del controlador, métodoreturnView presenta su vista y devuelve una cadena HTML para que pueda insertarla con JS / jQuery en su página cuando la reciba en el lado del cliente:

  public static string RenderPartialToString(string view, object model, ControllerContext Context)
        {
            if (string.IsNullOrEmpty(view))
            {
                view = Context.RouteData.GetRequiredString("action");
            }

            ViewDataDictionary ViewData = new ViewDataDictionary();

            TempDataDictionary TempData = new TempDataDictionary();

            ViewData.Model = model;

            using (StringWriter sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(Context, view);

                ViewContext viewContext = new ViewContext(Context, viewResult.View, ViewData, TempData, sw);

                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }

        //"Error" should be name of the partial view, I was just testing with partial error view
        //You can put whichever controller you want instead of HomeController it will be the same
        //You can pass model instead of null
        private string returnView()
        {
            var controller = new HomeController();
            controller.ControllerContext = new ControllerContext(HttpContext,new System.Web.Routing.RouteData(), controller);
            return RenderPartialToString("Error", null, new ControllerContext(controller.Request.RequestContext, controller));
        }

No lo probé en un Hub pero debería funcionar.

 formatc06 ago. 2012 21:01
@James El controlador puede ser cualquiera siempre que su vista parcial esté en una carpeta compartida, si no lo está, entonces tiene que usar la instancia del controlador a la que pertenece la vista parcial, ¿esto resuelve la referencia nula?
 James06 ago. 2012 20:18
Hmmm Inicialmente, parecía que funcionaría, pero cuando lo probé por completo, continúo teniendo problemas: no hay datos de ruta disponibles (ya que la solicitud no se enruta a través del enrutamiento MVC). Añadido enrutamiento falso en para hacer frente a esto. Ahora, obteniendo una excepción nula de referencia al tratar de encontrar una vista parcial ... sigue sintiendo que este enfoque está nadando contra la corriente. Voy a investigar algunas de las otras opciones (como los motores de plantillas de Razor) y ver dónde me lleva. Voy a actualizar este post a medida que descubra más.
 pixparker28 nov. 2017 17:03
cuando se llama desde la solicitud de signalr, dice: La respuesta no está disponible en este contexto
 Forty-Two31 jul. 2012 22:01
Utilizo una técnica similar para representar varias vistas como cadenas con el fin de enviarlas como mensajes de correo electrónico, y hasta la fecha se ha demostrado su eficacia. Lo único es que podría resultar complicado llamarlo desde fuera del controlador debido a ControllerContext en el parámetro.
 formatc07 ago. 2012 13:06
@James, ¿puedes compartir cómo debe ser la ruta?
 James07 ago. 2012 06:31
@ user1010609, funcionó! También logré que funcionara en la carpeta Controlador, solo necesitaba ajustar un poco el enrutamiento falso. Gracias por seguir con eso.
 Dave Lawrence22 oct. 2012 15:40
Por lo general, puedo resolver estas cosas, pero esta simplemente no puedo ponerme a trabajar. Tiene que haber una mejor manera de hacer esto. Me parece muy hacky.
 formatc01 ago. 2012 13:13
@ Cuarenta y dos Creo que debería ser bueno, ya que inicié la instancia del controlador antes de llamar a este método. Puede ver una instancia de ControllerContext hecha desde cero. Sería bueno que alguien lo probara solo para avisarme, ya que estoy de vacaciones y no tengo ningún sitio para probarlo.

nuación, sugeriría usar

Html.Partial (nombre de vista parcial)

Devuelve una MvcHtmlString, que debería poder usar como contenido de su respuesta de SignalR. Sin embargo, no he probado esto.

Pregunta de desbordamiento de pila:¿Es posible renderizar una vista fuera de un controlador?

 James26 jul. 2012 17:25
El tipo del objeto "HTML" es "HTMLHelper". Para instanciar esto, necesitas un ViewContext. Para crear una instancia de un ViewContext necesita un ControllerContext ... volver al mismo problema. No hay controlador. ¡Gracias por mirar sin embargo!

Probablemente la mejor opción es usarMaquinilla de afeitar, comoWim está sugiriendo

public class mySignalRHub: Hub
{
    public string getTableHTML()
    {
        var viewModel = new[] { new DataItem { Value1 = "v1", Value2 = "v2" } };

        var template = File.ReadAllText(Path.Combine(
            AppDomain.CurrentDomain.BaseDirectory,
            @"Views\PathToTablePartialView\_MyTablePartialView.cshtml"));

        return Engine.Razor.RunCompile(template, "templateKey", null, viewModel);
    }
}
 James06 ago. 2012 20:22
Parece interesante. Sin embargo, esta biblioteca tiene problemas con el uso concurrente, que no funcionará bien en un entorno web. Ver este tema para más detalles:stackoverflow.com/questions/6444277/…. El autor de este motor estaba haciendo una reescritura para abordar estos problemas, pero no parece haber habido ningún movimiento en este proyecto por un tiempo, podría ser un proyecto muerto ahora:github.com/Antaris/RazorEngine
 Paolo Moretti06 ago. 2012 22:12
@James RazorEngine v3 (el actualPaquete de NuGet) debería funcionar bien en un escenario multihilo. Solo necesitas crear una instancia deTemplateService, en lugar de invocar el método estáticoRazor.Parse. Miraresta publicación para más detalles.

Su respuesta a la pregunta