Search code examples
c#asp.net-mvcasp.net-core-2.1asp.net-core-signalr

Rendering Partial view as a string inside SignalR Hub


I am trying to dynamically load partial view contents in one of my views via a SignalR Hub to update client side data. For this I have to render the Partial view as a string in the SignalR Hub and send this string to the client side. I know how to render a partial view as a string inside a controller, but how would I do this outside of a controller?


Solution

  • I am trying to dynamically load partial view contents in one of my views via a SignalR Hub to update client side data. For this I have to render the Partial view as a string in the SignalR Hub and send this string to the client side.

    If you'd like to render your view/partial view as a html string within your hub method, you can refer to this blog with example that demonstrates how to render a Partial View to string.

    And I did a test with the example code RazorPartialToStringRenderer in my SignalR app, which work well for me.

    private readonly IRazorPartialToStringRenderer _renderer;
    public ChatHub(IRazorPartialToStringRenderer renderer)
    {
        _renderer = renderer;
    }
    public async Task SendMessage(string user, string message)
    {
    
        var view_result_mes = await _renderer.RenderPartialToStringAsync<string>("_SayHelloPartialView", null);
    
        //...
    

    Code referenced from above blog

    public interface IRazorPartialToStringRenderer
    {
        Task<string> RenderPartialToStringAsync<TModel>(string partialName, TModel model);
    }
    
    public class RazorPartialToStringRenderer : IRazorPartialToStringRenderer
    {
        private IRazorViewEngine _viewEngine;
        private ITempDataProvider _tempDataProvider;
        private IServiceProvider _serviceProvider;
        public RazorPartialToStringRenderer(
            IRazorViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _viewEngine = viewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }
        public async Task<string> RenderPartialToStringAsync<TModel>(string partialName, TModel model)
        {
            var actionContext = GetActionContext();
            var partial = FindView(actionContext, partialName);
            using (var output = new StringWriter())
            {
                var viewContext = new ViewContext(
                    actionContext,
                    partial,
                    new ViewDataDictionary<TModel>(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
                    {
                        Model = model
                    },
                    new TempDataDictionary(
                        actionContext.HttpContext,
                        _tempDataProvider),
                    output,
                    new HtmlHelperOptions()
                );
                await partial.RenderAsync(viewContext);
                return output.ToString();
            }
        }
        private IView FindView(ActionContext actionContext, string partialName)
        {
            var getPartialResult = _viewEngine.GetView(null, partialName, false);
            if (getPartialResult.Success)
            {
                return getPartialResult.View;
            }
            var findPartialResult = _viewEngine.FindView(actionContext, partialName, false);
            if (findPartialResult.Success)
            {
                return findPartialResult.View;
            }
            var searchedLocations = getPartialResult.SearchedLocations.Concat(findPartialResult.SearchedLocations);
            var errorMessage = string.Join(
                Environment.NewLine,
                new[] { $"Unable to find partial '{partialName}'. The following locations were searched:" }.Concat(searchedLocations)); ;
            throw new InvalidOperationException(errorMessage);
        }
        private ActionContext GetActionContext()
        {
            var httpContext = new DefaultHttpContext
            {
                RequestServices = _serviceProvider
            };
            return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
        }
    }
    

    Test Result

    enter image description here