Где находятся свойства ControllerContext и ViewEngines в MVC 6 Controller?

Я создал новый проект MVC6 и создаю новый сайт. Цель состоит в том, чтобы получить результат представления. Я нашел следующий код, но не могу заставить его работать, потому что не могу найти ControllerContext и ViewEngines,

Вот код, который я хочу переписать:

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

6 ответов

Решение

Обновление: я обновляю это для работы с.Net Core 2.x, так как API изменились с 2015 года!

Прежде всего, мы можем использовать встроенное внедрение зависимостей, которое поставляется с ASP.Net MVC Core, которое даст нам ICompositeViewEngine объект, который мы должны визуализировать наши взгляды вручную. Так, например, контроллер будет выглядеть так:

public class MyController : Controller
{
    private ICompositeViewEngine _viewEngine;

    public MyController(ICompositeViewEngine viewEngine)
    {
        _viewEngine = viewEngine;
    }

    //Rest of the controller code here
}

Далее код, который нам на самом деле нужен для визуализации представления. Обратите внимание, что теперь это async метод, поскольку мы будем делать асинхронные вызовы внутри:

private async Task<string> RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.ActionDescriptor.ActionName;

    ViewData.Model = model;

    using (var writer = new StringWriter())
    {
        ViewEngineResult viewResult = 
            _viewEngine.FindView(ControllerContext, viewName, false);

        ViewContext viewContext = new ViewContext(
            ControllerContext, 
            viewResult.View, 
            ViewData, 
            TempData, 
            writer, 
            new HtmlHelperOptions()
        );

        await viewResult.View.RenderAsync(viewContext);

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

И чтобы вызвать метод, это так просто:

public async Task<IActionResult> Index()
{
    var model = new TestModel
    {
        SomeProperty = "whatever"
    }

    var renderedView = await RenderPartialViewToString("NameOfView", model);

    //Do what you want with the renderedView here

    return View();
}

Выпущенное ядро ​​dotnet 1.0 изменилось, эта версия вышеприведенного кода работает с 1.0 RTM.

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.ActionDescriptor.DisplayName;

    ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
        var engine = _serviceProvider.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine; // Resolver.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
        ViewEngineResult viewResult = engine.FindView(ControllerContext, viewName, false);

        ViewContext viewContext = new ViewContext(
            ControllerContext,
            viewResult.View,
            ViewData,
            TempData,
            sw,
            new HtmlHelperOptions() //Added this parameter in
        );

        //Everything is async now!
        var t = viewResult.View.RenderAsync(viewContext);
        t.Wait();

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

Эти использования необходимы для этого кода для компиляции:

using System.IO;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;

Мне также пришлось добавить интерфейсы DI в конструктор контроллера:

IServiceProvider serviceProvider

Мой конструктор аккаунта теперь выглядит так:

public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory,
    IServiceProvider serviceProvider)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
    _serviceProvider = serviceProvider;
}

Решение Мартина Томеса работает хорошо. Мои изменения: удалил serviceProvider и получил ICompositeViewEngine в конструкторе через DI. Конструктор выглядит так:

private readonly ICompositeViewEngine _viewEngine;

        public AccountController(

                UserManager<ApplicationUser> userManager,
                SignInManager<ApplicationUser> signInManager,
                IEmailSender emailSender,
                ISmsSender smsSender,
                ILoggerFactory loggerFactory,
                ICompositeViewEngine viewEngine)
            {
                _userManager = userManager;
                _signInManager = signInManager;
                _emailSender = emailSender;
                _smsSender = smsSender;
                _logger = loggerFactory.CreateLogger<AccountController>();
                _viewEngine = viewEngine;;
            }

и положи

ViewEngineResult viewResult = _viewEngine.FindView(ControllerContext, viewName, false);

вместо

var engine = _serviceProvider.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine; // Resolver.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
ViewEngineResult viewResult = engine.FindView(ControllerContext, viewName, false);

Решение, предоставленное DavidG, работало нормально, но я изменил его на независимую службу.

      namespace WebApplication.Services
{
    public interface IViewRenderService
    {
        Task<string> RenderPartialViewToString(Controller Controller, string viewName, object model);
    }

    public class ViewRenderService: IViewRenderService
    {
        private readonly ICompositeViewEngine _viewEngine;

        public ViewRenderService(ICompositeViewEngine viewEngine)
        {
            _viewEngine = viewEngine;
        }
       public async Task<string> RenderPartialViewToString(Controller Controller, string viewName, object model)
            {
                if (string.IsNullOrEmpty(viewName))
                    viewName = Controller.ControllerContext.ActionDescriptor.ActionName;

                Controller.ViewData.Model = model;

                using (var writer = new StringWriter())
                {
                    ViewEngineResult viewResult =
                        _viewEngine.FindView(Controller.ControllerContext, viewName, false);

                    ViewContext viewContext = new ViewContext(
                        Controller.ControllerContext,
                        viewResult.View,
                        Controller.ViewData,
                        Controller.TempData,
                        writer,
                        new HtmlHelperOptions()
                    );

                    await viewResult.View.RenderAsync(viewContext);

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

Добавить сервис в автозагрузку:

      services.AddScoped<IViewRenderService, ViewRenderService>();

Теперь используйте инъекцию зависимостей следующим образом:

      public class MyController : Controller
{
private readonly IViewRenderService _viewRender;

public MyController(IViewRenderService viewRender)
{
    _viewRender = viewRender;
}

//Rest of the controller code here
}

Теперь вы можете использовать его в таком методе действий:

      var renderedView = await_viewRender.RenderPartialViewToString(this,"nameofview", model);

Решение, предоставленное Мартином Томесом, сработало для меня, но мне пришлось заменить:

ViewEngineResult viewResult = engine.FindView(ControllerContext, viewName, false);

с

ViewEngineResult viewResult = engine.GetView(_env.WebRootPath, viewName, false);

Также в конструктор контроллера пришлось добавить

private IHostingEnvironment _env;

public AccountController(IHostingEnvironment env)
{
    _env = env;
}

Я последовал вашим предложениям и сделал так, добавил метод в моем базовом контроллере, передал механизм просмотра из текущего контроллера, поэтому его легко использовать везде, где это может понадобиться:

Изображение из визуальной студии
Visual Studio (Нажмите на изображение, чтобы увеличить)

Другие вопросы по тегам