Где находятся свойства 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;
}
Я последовал вашим предложениям и сделал так, добавил метод в моем базовом контроллере, передал механизм просмотра из текущего контроллера, поэтому его легко использовать везде, где это может понадобиться: