我正在使用新的 ASP.Net 5/MVC 6 库构建一个项目,现在我想要将 razor 页面动态呈现为字符串。
这是针对动态小部件系统的,因此我们的想法是在生成模型之后、渲染主视图之前在 ActionFilter
中执行此操作。
它的设置相当大,但主要需要服务并将各个部分组合在一起(如果有人知道更简单的方法,请告诉我们!)。
public override void OnActionExecuted(ActionExecutedContext context) {
var pageFactory = (IRazorPageFactory)context.HttpContext.ApplicationServices.GetService(typeof(IRazorPageFactory));
var viewEngine = (IRazorViewEngine)context.HttpContext.ApplicationServices.GetService(typeof(IRazorViewEngine));
var viewFactory = (IRazorViewFactory)context.HttpContext.ApplicationServices.GetService(typeof(IRazorViewFactory));
var modelMetadataProvider = (IModelMetadataProvider)context.HttpContext.ApplicationServices.GetService(typeof(IModelMetadataProvider));
var page = pageFactory.CreateInstance($"~/Views/Widgets/{widgetInfo.Type}.cshtml");
var view = viewFactory.GetView(viewEngine, page, false);
var sb = new StringBuilder();
var sw = new StringWriter(sb);
page.ViewContext = new ViewContext(new ActionContext(context.HttpContext, context.RouteData, context.ActionDescriptor), view, new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary()), sw);
完成所有这些设置后,我尝试在 ViewData 上设置 Model
属性,我可以看到我已经设置了正确的对象,并且它是页面的正确类型。
page.ViewContext.ViewData.Model = widgetInfo.Setting;
然后,我执行该页面,当我在模板中未使用任何模型时,该页面效果很好。
page.ExecuteAsync().GetAwaiter().GetResult();
sw.Flush();
var renderedWidget = sb.ToString();
我可以看到我的模板已加载(甚至在其中放置了断点),但模型始终为空。目前我使用这个简单的模板。
@model DataObjects.CodeWidgetModel
@Html.Raw(Model.Code)
我一直在搜索MVC6-sources在 GitHub 上查看我可能错过的内容,但我找不到任何有用的内容。
我正在运行 ASP.Net MVC 6.0.0-beta3-12628
和 KRE 1.0.0-beta3-11014
,但很可能会尽快升级到 beta4因为它可用。
任何人都知道为什么在 ViewData
上设置 Model
还不够,或者我还需要做什么才能使其正常工作?
最佳答案
经过更多研究(老实说,经过大量试验和错误),似乎RazorPage.ExecuteAsync()
这不是正确的方法。调用RazorView.RenderAsync()
相反,正确输入 ViewDataDictionary<>
成功了!
下面是最终的代码。
public override void OnActionExecuted(ActionExecutedContext context) {
var pageFactory = (IRazorPageFactory)context.HttpContext.ApplicationServices.GetService(typeof(IRazorPageFactory));
var viewEngine = (IRazorViewEngine)context.HttpContext.ApplicationServices.GetService(typeof(IRazorViewEngine));
var viewFactory = (IRazorViewFactory)context.HttpContext.ApplicationServices.GetService(typeof(IRazorViewFactory));
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var page = pageFactory.CreateInstance($"~/Areas/Widgets/DefaultViews/Widgets/{widgetInfo.Type}.cshtml");
var view = (RazorView)viewFactory.GetView(viewEngine, page, true);
var vddType = typeof(ViewDataDictionary<>);
var vddGeneric = vddType.MakeGenericType(widgetInfo.Model.GetType());
dynamic viewDataDictionary = Activator.CreateInstance(vddGeneric, new EmptyModelMetadataProvider(), new ModelStateDictionary());
var actionContext = new ActionContext(context.HttpContext, context.RouteData, context.ActionDescriptor);
var viewContext = new ViewContext(actionContext, view, viewDataDictionary, sw);
viewContext.ViewData.Model = widgetInfo.Model;
view.RenderAsync(viewContext).GetAwaiter().GetResult();
sw.Flush();
var renderedWidget = sb.ToString();
}
我对 dynamic
不是特别满意对于 ViewDataDictionary
但它有效。可能还有其他更简单的方法可以做到这一点,但目前已经足够了。
在 ASP.Net MVC 6.0.0-beta3-12628
上测试和KRE 1.0.0-beta3-11014
.
关于c# - 在 ASP.Net 5 中渲染 RazorPage 时无法设置模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29565256/