ASP.NET Core 3.1局部视图遍历Model报空引用异常
问题根本原因
出现这个空引用错误核心是认知偏差加代码缺漏:
- 调用
PartialAsync渲染局部视图时,你只传入了视图路径,没有给强类型绑定的IEnumerable<ProductViewModel>模型传值。PartialAsync的逻辑是直接渲染指定的cshtml文件,不会自动执行对应控制器的Action方法,也不会主动调用你的仓储层查询方法,没有显式传入模型时,视图的Model属性默认就是null,直接对null执行foreach遍历必然抛出空引用异常。 - 你虽然在仓储层写了数据查询方法,但既没有在渲染局部视图前调用该方法拿到数据,也没有将数据作为参数传入局部视图,局部视图根本无法获取到你查询的产品列表数据。
- 额外注意:不要混淆旧版ASP.NET MVC的
RenderAction行为和ASP.NET Core的PartialAsync行为,前者会执行指定Action的逻辑返回渲染结果,后者仅做视图文件的直接渲染,不会触发任何Action、服务层的自动执行。也不要依赖普通业务Action返回的模型给布局页的局部视图传值——布局页是所有页面共用的,不可能每个Action都查询产品滑块数据,只要有一个页面不传对应模型,就会触发空引用错误。
解决方法
根据实现规范程度,可选以下几种方案:
方案1:直接在布局页显式传模型(快速修复)
适合临时快速实现需求的场景,步骤如下:
- 在布局页
_Layout_Pigall.cshtml顶部注入你的产品仓储服务:
@inject IProductRepositories ProductRepository
- 修改
PartialAsync调用代码,将查询到的数据作为第二个参数传入局部视图:
@await Html.PartialAsync("/Views/Products/MyIndex.cshtml", await ProductRepository.GetDataForProductViewModels())
- (建议加兜底)在局部视图
MyIndex.cshtml中增加null值防护,避免极端场景下再次抛错:
@model IEnumerable<DataAccessLayer.ViewModels.ProductViewModel> @{ // 模型为null时使用空列表兜底 var products = Model ?? Enumerable.Empty<DataAccessLayer.ViewModels.ProductViewModel>(); } <!-- 后续遍历products即可,不要直接遍历Model --> <section class="container-fluid p-0 slider-section"> <div class="row slider"> <div class="col-12 p-0 slide-right-banner"> <div class="owl-carousel owl-theme owl-slider"> @foreach (var item in products) { <a class="item" href=""> <img src="images/@item.ImageName" alt="" class="d-none d-md-block"> <img src="images/@item.ImageName" alt="" class="d-xl-none d-lg-none -md-none"> </a> <a class="item" href=""> <img src="images/slide2.jpg" alt="" class="d-none d-md-block"> <img src="images/slider-mob2.jpg" alt="" class="d-xl-none d-lg-none d-md-none"> </a> } </div> </div> </div> </section>
方案2:使用视图组件实现(推荐规范做法)
布局页中需要渲染独立数据的公共模块,ASP.NET Core官方推荐使用视图组件(ViewComponent)实现,避免在视图文件中耦合数据查询逻辑,职责更清晰,也更方便维护:
- 新建产品滑块视图组件类,继承
ViewComponent,在类中注入仓储服务、编写数据查询逻辑:
public class ProductSliderViewComponent : ViewComponent { private readonly IProductRepositories _productRepo; public ProductSliderViewComponent(IProductRepositories productRepo) { _productRepo = productRepo; } public async Task<IViewComponentResult> InvokeAsync() { var data = await _productRepo.GetDataForProductViewModels(); return View(data); } }
- 将原来的
MyIndex.cshtml局部视图移动到/Views/Shared/Components/ProductSlider/Default.cshtml路径下,视图代码不需要做修改(保留null兜底逻辑更好)。 - 在布局页原来调用局部视图的位置,替换为视图组件调用代码:
@await Component.InvokeAsync("ProductSlider")
这种写法不需要在视图中直接注入仓储、写查询逻辑,数据查询逻辑都封装在视图组件类中,符合MVC的职责分离原则。
内容的提问来源于stack exchange,提问作者Mohammad Sahraei
相关产品推荐
相关产品推荐

