Мне удалось его взломать! В конце концов, я был на неправильном пути ... решение - использовать ViewComponent
. Но это все еще круто!
Благодаря
Решение
Преобразование PageModel в ViewComponent
namespace MyProject.ViewComponents
{
public class MyViewComponent : ViewComponent
{
private readonly MyDbContext _context;
public MyViewComponent(MyDbContext context)
{
_context = context;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var count = await _context.Foos.CountAsync();
var message = $"Server time is { DateTime.Now } and the Foo count is { count }";
return View<string>(message);
}
}
}
и представление помещается в Pages / Shared / Components / My / Default.cs html
@model string
<h2>Test</h2>
<p>
@Model
</p>
Служба
using System;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
public class RenderViewComponentService
{
private readonly IServiceProvider _serviceProvider;
private readonly ITempDataProvider _tempDataProvider;
private readonly IViewComponentHelper _viewComponentHelper;
public RenderViewComponentService(
IServiceProvider serviceProvider,
ITempDataProvider tempDataProvider,
IViewComponentHelper viewComponentHelper
)
{
_serviceProvider = serviceProvider;
_tempDataProvider = tempDataProvider;
_viewComponentHelper = viewComponentHelper;
}
public async Task<string> RenderViewComponentToStringAsync<TViewComponent>(object args)
where TViewComponent : ViewComponent
{
var viewContext = GetFakeViewContext();
(_viewComponentHelper as IViewContextAware).Contextualize(viewContext);
var htmlContent = await _viewComponentHelper.InvokeAsync<TViewComponent>(args);
using var stringWriter = new StringWriter();
htmlContent.WriteTo(stringWriter, HtmlEncoder.Default);
var html = stringWriter.ToString();
return html;
}
private ViewContext GetFakeViewContext(ActionContext actionContext = null, TextWriter writer = null)
{
actionContext ??= GetFakeActionContext();
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
var tempData = new TempDataDictionary(actionContext.HttpContext, _tempDataProvider);
var viewContext = new ViewContext(
actionContext,
NullView.Instance,
viewData,
tempData,
writer ?? TextWriter.Null,
new HtmlHelperOptions());
return viewContext;
}
private ActionContext GetFakeActionContext()
{
var httpContext = new DefaultHttpContext
{
RequestServices = _serviceProvider,
};
var routeData = new RouteData();
var actionDescriptor = new ActionDescriptor();
return new ActionContext(httpContext, routeData, actionDescriptor);
}
private class NullView : IView
{
public static readonly NullView Instance = new NullView();
public string Path => string.Empty;
public Task RenderAsync(ViewContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
return Task.CompletedTask;
}
}
}
Использование
Со страницы Razor (страница отладки / настройки)
Примечание за файлом нет кода
@page
@using MyProject.ViewComponents
@await Component.InvokeAsync(typeof(MyViewComponent))
With RouteData
@page "{id}"
@using MyProject.ViewComponents
@await Component.InvokeAsync(typeof(MyViewComponent), RouteData.Values["id"])
From Controller
[HttpGet]
public async Task<IActionResult> Get()
{
var html = await _renderViewComponentService
.RenderViewComponentToStringAsync<MyViewComponent>();
// do something with the html
return Ok(new { html });
}
With FromRoute
[HttpGet("{id}")]
public async Task<IActionResult> Get([FromRoute] int id)
{
var html = await _renderViewComponentService
.RenderViewComponentToStringAsync<MyViewComponent>(id);
// do something with the html
return Ok(new { html });
}
Странный
Очень жаль, что введенный IViewComponentHelper
не работает из коробки.
Итак, нам пришлось сделать очень неинтуитивную вещь , чтобы заставить ее работать.
(_viewComponentHelper as IViewContextAware).Contextualize(viewContext);
что вызывает каскад странных вещей, таких как подделка ActionContext
и ViewContext
, которые требуют TextWriter
, но НИЧЕГО не используются! На самом деле отверстие ViewContext
вообще не используется. Он просто должен существовать: (
Также NullView
... по какой-то причине Microsoft.AspNetCore.Mvc.ViewFeatures.NullView
равен Internal
, поэтому мы должны скопировать / вставить его в наш собственный код: (
Возможно, в будущем он будет улучшен.
В любом случае ... ИМО, это проще, чем использовать IRazorViewEngine
, которое появляется практически в каждом поиске Google:)
Надеюсь на это может кому-то помочь.