ASP.NET Core中Index经_Layout传模型至_Header引发空引用异常
问题描述
基于ASP.NET Core 8.0的Razor Pages项目中,访问http://localhost:5###/时,尝试通过ViewData["_Header"]从Index页面经_Layout页面传递数据到_Header页面,_Header页面在get_Model()处抛出NullReferenceException。调试发现ViewData["_Header"]值正常,但执行Html.PartialAsync("_Header", ViewData["_Header"])后模型变为null。
相关代码
Index.cshtml
@page @{ Layout = "_Layout"; ViewData["_Header"] = new DemoForStackOverflow.Pages.Shared.Header() {IsArriving = true}; } <h2>some content</h2>
_Layout.cshtml
<!DOCTYPE html> <html> <head> <title>Demo for Stack Overflow</title> </head> <body> <div id="header"> @await Html.PartialAsync("_Header", ViewData["_Header"]) </div> <h1>Appears on every page below header and above body</h1> <div id="body"> @RenderBody() </div> </body> </html>
_Header.cshtml
@page @model DemoForStackOverflow.Pages.Shared.Header @if (Model.IsArriving) { <p>Hello</p> } else { <p>Goodbye</p> }
_Header.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages; namespace DemoForStackOverflow.Pages.Shared; public class Header : PageModel { public bool IsArriving { get; set; } }
异常信息
> Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] > > An unhandled exception has occurred while executing the request. > > System.NullReferenceException: Object reference not set to an instance of an object. > > at AspNetCoreGeneratedDocument.Pages_Shared__Header.get_Model() > at AspNetCoreGeneratedDocument.Pages_Shared__Header.ExecuteAsync() in /home/RF/DemoForStackOverflow/Pages/Shared/_Header.cshtml:line 4 > at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) > at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts) > at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context) at Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.RenderPartialCoreAsync(String partialViewName, Object model, ViewDataDictionary viewData, TextWriter writer) at Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.PartialAsync(String partialViewName, Object model, ViewDataDictionary viewData) at AspNetCoreGeneratedDocument.Pages_Shared__Layout.<ExecuteAsync>b__8_1() in /home/RF/DemoForStackOverflow/Pages/Shared/_Layout.cshtml:line 8 at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() at AspNetCoreGeneratedDocument.Pages_Shared__Layout.ExecuteAsync() at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderLayoutAsync(ViewContext context, ViewBufferTextWriter bodyWriter) at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode) at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
问题原因
_Header.cshtml中添加了@page指令,这会将该文件标记为独立的Razor Page,而非Partial View。当通过Html.PartialAsync调用时,Razor会初始化该Page的PageModel(即Header类),而忽略传入的模型参数,导致Model属性为空,最终触发NullReferenceException。
解决方案
修复现有代码
只需移除_Header.cshtml中的@page指令,将其改为Partial View:
@model DemoForStackOverflow.Pages.Shared.Header @if (Model.IsArriving) { <p>Hello</p> } else { <p>Goodbye</p> }
此时Html.PartialAsync传入的模型会正确绑定到Model属性,异常即可解决。
更优实现方式
方式1:使用ViewComponent
ViewComponent是Razor Pages中更适合复用组件的方案,支持独立逻辑和视图:
- 创建
HeaderViewComponent.cs:
using Microsoft.AspNetCore.Mvc; namespace DemoForStackOverflow.ViewComponents; public class HeaderViewComponent : ViewComponent { public IViewComponentResult Invoke(bool isArriving) { return View(isArriving); } }
- 在
Pages/Shared/Components/Header/Default.cshtml创建视图:
@model bool @if (Model) { <p>Hello</p> } else { <p>Goodbye</p> }
- 在
_Layout.cshtml中调用:
<div id="header"> @await Component.InvokeAsync("Header", new { isArriving = ViewData["_HeaderIsArriving"] }) </div>
- 在
Index.cshtml中设置数据:
@{ ViewData["_HeaderIsArriving"] = true; }
方式2:直接使用ViewData传递
无需创建PageModel类,直接通过ViewData传递值:
Index.cshtml中设置:
@{ ViewData["IsArriving"] = true; }
_Header.cshtml(移除@page和@model)中直接读取:
@if ((bool)ViewData["IsArriving"]) { <p>Hello</p> } else { <p>Goodbye</p> }
_Layout.cshtml中调用Partial时无需传递模型:
<div id="header"> @await Html.PartialAsync("_Header") </div>
内容的提问来源于stack exchange,提问作者RF Eggnine
相关产品推荐
相关产品推荐

