在 ASP.Net 5 中呈现 RazorPage 时无法设置模型
Can't set model when rendering RazorPage in ASP.Net 5
我正在使用新的 ASP.Net 5/MVC 6 库构建一个项目,我已经到了要将剃刀页面动态呈现为字符串的部分。
这是针对动态小部件系统的,因此我们的想法是在生成模型之后但在渲染主视图之前 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)
我一直在 GitHub 搜索 MVC6-sources 以查看我可能遗漏了什么,但我找不到任何有用的东西。
我是 运行 ASP.Net MVC 6.0.0-beta3-12628
和 KRE 1.0.0-beta3-11014
,但很可能会在可用时尽快升级到 beta4。
任何人都知道为什么在 ViewData
上设置 Model
是不够的,或者我还需要做什么才能让它工作?
为了如此简单的事情,您正在深入研究渲染管道。尝试使用小部件集合创建模型。在您的主视图中执行以下操作:
foreach(var widget in Model.Widgets) { Html.DisplayFor(widget) }
然后在 /views/displaytemplates/widget.cshtml 中创建一个视图,然后将小部件转换为更具体类型的小部件,然后用它调用 Html.DisplayFor,从而将小部件分解为正确的模板。
** 伪代码 **
@model Widget
if (Widget is Widget1Type) Html.DisplayFor((Widget1Type)Widget)
if (Widget is Widget2Type) Html.DisplayFor((Widget2Type)Widget)
if (Widget is Widget3Type) Html.DisplayFor((Widget3Type)Widget)
if (Widget is Widget4Type) Html.DisplayFor((Widget4Type)Widget)
然后为这些小部件创建视图
/views/displaytemplates/widget1类型:
@model Widget1Type
...Stuff here...
经过更多研究(老实说,进行了大量的试验和错误)后,RazorPage.ExecuteAsync()
似乎不是正确的方法。使用正确键入的 ViewDataDictionary<>
调用 RazorView.RenderAsync()
就成功了!
下面是最终代码。
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();
}
我对 ViewDataDictionary
的 dynamic
不是特别满意,但它确实有效。可能还有其他更简单的方法可以做到这一点,但现在已经足够了。
在 ASP.Net MVC 6.0.0-beta3-12628
和 KRE 1.0.0-beta3-11014
上测试。
我正在使用新的 ASP.Net 5/MVC 6 库构建一个项目,我已经到了要将剃刀页面动态呈现为字符串的部分。
这是针对动态小部件系统的,因此我们的想法是在生成模型之后但在渲染主视图之前 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)
我一直在 GitHub 搜索 MVC6-sources 以查看我可能遗漏了什么,但我找不到任何有用的东西。
我是 运行 ASP.Net MVC 6.0.0-beta3-12628
和 KRE 1.0.0-beta3-11014
,但很可能会在可用时尽快升级到 beta4。
任何人都知道为什么在 ViewData
上设置 Model
是不够的,或者我还需要做什么才能让它工作?
为了如此简单的事情,您正在深入研究渲染管道。尝试使用小部件集合创建模型。在您的主视图中执行以下操作:
foreach(var widget in Model.Widgets) { Html.DisplayFor(widget) }
然后在 /views/displaytemplates/widget.cshtml 中创建一个视图,然后将小部件转换为更具体类型的小部件,然后用它调用 Html.DisplayFor,从而将小部件分解为正确的模板。
** 伪代码 **
@model Widget
if (Widget is Widget1Type) Html.DisplayFor((Widget1Type)Widget)
if (Widget is Widget2Type) Html.DisplayFor((Widget2Type)Widget)
if (Widget is Widget3Type) Html.DisplayFor((Widget3Type)Widget)
if (Widget is Widget4Type) Html.DisplayFor((Widget4Type)Widget)
然后为这些小部件创建视图 /views/displaytemplates/widget1类型:
@model Widget1Type
...Stuff here...
经过更多研究(老实说,进行了大量的试验和错误)后,RazorPage.ExecuteAsync()
似乎不是正确的方法。使用正确键入的 ViewDataDictionary<>
调用 RazorView.RenderAsync()
就成功了!
下面是最终代码。
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();
}
我对 ViewDataDictionary
的 dynamic
不是特别满意,但它确实有效。可能还有其他更简单的方法可以做到这一点,但现在已经足够了。
在 ASP.Net MVC 6.0.0-beta3-12628
和 KRE 1.0.0-beta3-11014
上测试。