0

I want to create a common message box method that can be called throughout my ASP.NET 5 application

The code should be something like this:

public class WebHelper
{
    public static IActionResult MessageBox(string message)
    {
        return PartialView("~/Views/Shared/_MessageBox.cshtml", message);
    }
}

And then in my controller call:

return WebHelper.MessageBox("Hallo world");

instead of what I am currently doing of :

 return PartialView("~/Views/Shared/_MessageBox.cshtml", "Hallo world");

However PartialView is bound to the controller so it seems I cannot use it outside the controller.

I tried to use Answers 1 Answers 2 which are solutions for ASP.NET 4 but they don't work in ASP.NET 5 e.g.

 public string RenderPartialViewToString(string viewName, object model)
    {
        this.ViewData.Model = model;
        try
        {
            using (StringWriter sw = new StringWriter())
            {
                ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(this.ControllerContext, viewName);
                ViewContext viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, sw);
                viewResult.View.Render(viewContext, sw);

                return sw.GetStringBuilder().ToString();
            }
        }
        catch (System.Exception ex)
        {
            return ex.ToString();
        }
    }

as I can't find the ViewEngines.Engine in the ASP.NET 5 namespaces.

Question: What is the simplest way to solve my problem in `ASP.NET 5? Providing the method and calling method code

Community
  • 1
  • 1
dfmetro
  • 4,462
  • 8
  • 39
  • 65
  • There's a sample around rendering views to strings here - https://github.com/aspnet/Entropy/tree/dev/samples/Mvc.RenderViewToString – Pranav Jan 25 '16 at 15:40

2 Answers2

3

Here's the helper class that renders the view: (as you can see, the ViewEngines.Engine part that you couldn't find anymore is now injected as IRazorViewEngine)

using System;
using System.IO;
using Microsoft.AspNet.Http.Internal;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Abstractions;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.ViewFeatures;
using Microsoft.AspNet.Routing;

namespace WebApp.Demo.Helpers
{
    public class RazorViewToStringRenderer
    {
        private IRazorViewEngine _viewEngine;
        private ITempDataProvider _tempDataProvider;
        private IServiceProvider _serviceProvider;

        public RazorViewToStringRenderer(
            IRazorViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _viewEngine = viewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }

        public string RenderViewToString<TModel>(string name, TModel model)
        {
            var actionContext = GetActionContext();

            var viewEngineResult = _viewEngine.FindView(actionContext, name);

            if (!viewEngineResult.Success)
            {
                throw new InvalidOperationException(string.Format("Couldn't find view '{0}'", name));
            }

            var httpContextAccessor = new HttpContextAccessor();

            httpContextAccessor.HttpContext = actionContext.HttpContext;

            var view = viewEngineResult.View;

            using (var output = new StringWriter())
            {
                var viewContext = new ViewContext(
                    actionContext,
                    view,
                    new ViewDataDictionary<TModel>(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
                    {
                        Model = model
                    },
                    new TempDataDictionary(
                        httpContextAccessor,
                        _tempDataProvider),
                    output,
                    new HtmlHelperOptions());

                view.RenderAsync(viewContext).GetAwaiter().GetResult();

                return output.ToString();
            }
        }

        private ActionContext GetActionContext()
        {
            var httpContext = new DefaultHttpContext();
            httpContext.RequestServices = _serviceProvider;
            return new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
        }
    }
}

In Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
       // Add MVC services to the services container.
       services.AddMvc();
       // Add the helper rendering class as singleton
       services.AddSingleton<RazorViewToStringRenderer>();
}

Get injected helper class in controller for example:

public HomeController(RazorViewToStringRenderer renderer)
{
       string message = "Hallo world";

       var partialView = renderer.RenderViewToString("~/Views/Shared/_MessageBox.cshtml", message);
}

Code from here, with a few tweaks to work with the old packages like "Microsoft.AspNet.Mvc": "6.0.0-rc1-final": https://github.com/aspnet/Entropy/tree/dev/samples/Mvc.RenderViewToString

If you're using the new "Microsoft.AspNetCore.Mvc": "1.0.0-*", just get the code from there

Cristi Pufu
  • 9,002
  • 3
  • 37
  • 43
1

A bit late to the party, but I came across the same issue and the links in the previous answers and comments now seem to be broken.

A solution that I've used to get around this is to create an abstract class which inherits from 'Controller'.

    public abstract class MyControllerBase : Controller
    {
        protected IActionResult MessageBox(string message)
        {
            return PartialView("~/Views/Shared/_MessageBox.cshtml", message);
        }
    {

Just make sure that your controllers inherit from this base class and then in your controller you just need:

return MessageBox("Hello world");
jem
  • 61
  • 8