I can't find a way to render a partial view inside a signalr hub, the real problem is that i don't know how i would convert the partial to a string.
I made a table partial that renders some data from my database, i want that data to be refreshed each 2 minutes, i using signalr hub for this purpose. But i need to pass my partial as a string to the hub, otherwise it won't work. Any ideas?
Hub Code (I was trying to use a function but it didnt work):
public async Task<string> Teste()
{
var body = await _renderer.RenderPartialToStringAsync("../Areas/Admin/Pages/Partial/_Filas.cshtml", new principalModel());
return body.ToString();
}
You can use this class to render view to string :
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace RenderView.Models
{
public class ViewRenderer
{
private readonly IRazorViewEngine _viewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
private readonly IHttpContextAccessor _httpContextAccessor;
public ViewRenderer(
IRazorViewEngine viewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider,
IHttpContextAccessor httpContextAccessor)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
_httpContextAccessor = httpContextAccessor;
}
public async Task<string> RenderViewToStringAsync<TModel>(string viewNameOrPath, TModel model)
{
var actionContext = GetActionContext();
var view1 = _viewEngine.FindView(actionContext, viewNameOrPath, false);
if (!view1.Success)
{
view1 = _viewEngine.GetView("~/", viewNameOrPath, false);
if (!view1.Success)
throw new FileNotFoundException($"Couldn't find '{(object)viewNameOrPath}'");
}
var view2 = view1.View;
await using var output = new StringWriter();
var viewData = new ViewDataDictionary<TModel>((IModelMetadataProvider)new EmptyModelMetadataProvider(),
new ModelStateDictionary())
{
Model = model
};
var context = new ViewContext(actionContext, view2, (ViewDataDictionary)viewData,
(ITempDataDictionary)new TempDataDictionary(actionContext.HttpContext, this._tempDataProvider),
(TextWriter)output, new HtmlHelperOptions());
await view2.RenderAsync(context);
var stringAsync = output.ToString();
return stringAsync;
}
private ActionContext GetActionContext()
{
var httpContext = _httpContextAccessor?.HttpContext;
if (httpContext != null)
return new ActionContext(httpContext, httpContext.GetRouteData(), new ActionDescriptor());
var defaultHttpContext = new DefaultHttpContext
{
RequestServices = this._serviceProvider
};
return new ActionContext((HttpContext)defaultHttpContext, new RouteData(), new ActionDescriptor());
}
}
}
And you should add this lines to startup:
builder.Services.AddMvcCore().AddRazorViewEngine();
builder.Services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddScoped<ViewRenderer>();
Use ViewRenderer class as follows:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ViewRenderer _viewRenderer;
public HomeController(ILogger<HomeController> logger, ViewRenderer viewRenderer)
{
_logger = logger;
_viewRenderer = viewRenderer;
}
public async Task<IActionResult> Index()
{
var renderedView =await
_viewRenderer.RenderViewToStringAsync<MyViewModel>("_MyView", new MyViewModel() { Id = 1 });
ViewData["RenderedView"]= renderedView;
return View();
}
public class MyViewModel
{
public int Id { get; set; }
}
}