ASP.NET Razor模型属性被重复调用数百次的性能问题排查
问题描述
我是ASP.NET Razor新手,可能存在概念误解。我实例化模型并从本地数据库获取数据返回视图,代码可正常运行,但模型的MainNodes属性的getter被调用数百次,严重影响性能。调试时发现,在视图中访问@Model.MainNodes[i].name时模型会被重建,请问这是什么原因?
控制器代码
public class GlobalTaggingController : BaseCmsEditorController { public GlobalTaggingViewModel model = new GlobalTaggingViewModel(); public ActionResult Index(string searchword = "") { return View(model); } // ... 省略部分代码 }
ViewModel代码
public class GlobalTaggingViewModel : BaseViewModel { public List<NodeModel> MainNodes { get { var globalTaggingLocalizedManager = new GlobalTaggingLocalizationManager(); var manager = new GlobalTaggingManager(); var mainlevel = manager.GetAllItemsOnLevel(0); var subLevel = manager.GetAllItemsOnLevel(1); var subsubLevel = manager.GetAllItemsOnLevel(2); var resMainCat = new List<NodeModel>(); // ... 省略部分代码 return resMainCat; } } }
视图代码(片段)
@model Application.Areas.Administration.Models.GlobalTaggingViewModel @{ ViewBag.Title = "Administration - Video"; var positionNr = 0; } <form action="@Url.Action("Index")" method="POST" id="mainForm"> <div class="divHeader Administration"> <div class="divHeaderInner"> @for (int i = 0; i < Model.MainNodes.Count(); i++) { string catId = "CatId" + i.ToString(); string classToHideCatId = "classToHideCatId" + i.ToString(); string rowCounter = "rowCounter" + i.ToString(); <tr class="mainCatRow"> <td> <table class="mytable"> <thead> </thead> <tr class="@rowCounter rowHeight"> <td class="column0"> <div> <img src="@Url.Content("~/Content/images/icons/icon-open.png")" id="@catId" class="buttonLayoutMain" /> </div> </td> <td class="column1"> <table class="tableInCol1"> <tr> <td class="editButtonClassLeft"> <div class="mainCat"> @Model.MainNodes[i].name </div> </td> <td class="editButtonClassRight"> <div> <a onclick="javascript:Base.Popup.Open('@Url.Action("Index", "GlobalTaggingEditor", new {area = "Cms", id = Model.MainNodes[i].Id})', 1236, 800); return false;" href="#" class="ButtonEdit"><span class="mif-pencil"></span></a> </div> </td> </tr> </table> <td> <td> <div class="mainCat mainCol"> @Model.MainNodes[i].nameEng </div> <td> <td> <div class="mainCat mainCol"> @Model.MainNodes[i].name </div> <td> <td> <div class="mainCat mainCol"> @Model.MainNodes[i].nameSpanisch </div> <td> <td> <div class="mainCat mainCol"> @Model.MainNodes[i].nameFra </div> <td> <td> <div class="mainCat mainCol"> @Model.MainNodes[i].namePort </div> <td> <td> <div class="mainCat mainCol"> @Model.MainNodes[i].nameRuss </div> <td class="column9">
原因分析与解决方案
核心原因
你当前的MainNodes是一个仅包含getter的计算属性,每次访问Model.MainNodes(包括调用Count()、通过[i]索引取值)都会执行getter内的全部逻辑:重新实例化管理器、查询数据库、构建新的List<NodeModel>。
结合你的视图代码:
- 循环条件里的
Model.MainNodes.Count()→ 每次循环迭代都会触发一次getter - 循环体内多次调用
Model.MainNodes[i].xxx→ 每一次索引访问都会触发一次getter
累计下来触发数百次调用完全符合逻辑,性能自然会受严重影响。
解决方法
方法1:懒加载+缓存结果
修改ViewModel,用私有字段缓存查询结果,仅在第一次访问时执行数据库查询:
public class GlobalTaggingViewModel : BaseViewModel { private List<NodeModel> _mainNodes; // 私有字段缓存结果 public List<NodeModel> MainNodes { get { if (_mainNodes == null) // 仅第一次访问时执行查询逻辑 { var globalTaggingLocalizedManager = new GlobalTaggingLocalizationManager(); var manager = new GlobalTaggingManager(); var mainlevel = manager.GetAllItemsOnLevel(0); var subLevel = manager.GetAllItemsOnLevel(1); var subsubLevel = manager.GetAllItemsOnLevel(2); _mainNodes = new List<NodeModel>(); // ... 省略部分代码 } return _mainNodes; } } }
方法2:控制器提前加载数据
把数据加载逻辑从ViewModel的getter移到控制器中,避免视图重复触发查询:
- 修改ViewModel为普通可赋值属性:
public class GlobalTaggingViewModel : BaseViewModel { public List<NodeModel> MainNodes { get; set; } }
- 控制器内提前查询并赋值:
public class GlobalTaggingController : BaseCmsEditorController { public ActionResult Index(string searchword = "") { var model = new GlobalTaggingViewModel(); // 提前执行数据加载逻辑 var globalTaggingLocalizedManager = new GlobalTaggingLocalizationManager(); var manager = new GlobalTaggingManager(); var mainlevel = manager.GetAllItemsOnLevel(0); var subLevel = manager.GetAllItemsOnLevel(1); var subsubLevel = manager.GetAllItemsOnLevel(2); model.MainNodes = new List<NodeModel>(); // ... 省略部分代码 return View(model); } // ... 省略部分代码 }
方法3:视图内缓存局部变量
在视图开头将MainNodes缓存到局部变量,避免多次访问Model.MainNodes:
@model Application.Areas.Administration.Models.GlobalTaggingViewModel @{ ViewBag.Title = "Administration - Video"; var positionNr = 0; var mainNodes = Model.MainNodes; // 缓存到局部变量,仅触发一次getter } <form action="@Url.Action("Index")" method="POST" id="mainForm"> <div class="divHeader Administration"> <div class="divHeaderInner"> @for (int i = 0; i < mainNodes.Count(); i++) { string catId = "CatId" + i.ToString(); string classToHideCatId = "classToHideCatId" + i.ToString(); string rowCounter = "rowCounter" + i.ToString(); <tr class="mainCatRow"> <td> <table class="mytable"> <thead> </thead> <tr class="@rowCounter rowHeight"> <td class="column0"> <div> <img src="@Url.Content("~/Content/images/icons/icon-open.png")" id="@catId" class="buttonLayoutMain" /> </div> </td> <td class="column1"> <table class="tableInCol1"> <tr> <td class="editButtonClassLeft"> <div class="mainCat"> @mainNodes[i].name </div> </td> <td class="editButtonClassRight"> <div> <a onclick="javascript:Base.Popup.Open('@Url.Action("Index", "GlobalTaggingEditor", new {area = "Cms", id = mainNodes[i].Id})', 1236, 800); return false;" href="#" class="ButtonEdit"><span class="mif-pencil"></span></a> </div> </td> </tr> </table> <td> <td> <div class="mainCat mainCol"> @mainNodes[i].nameEng </div> <td> <td> <div class="mainCat mainCol"> @mainNodes[i].name </div> <td> <td> <div class="mainCat mainCol"> @mainNodes[i].nameSpanisch </div> <td> <td> <div class="mainCat mainCol"> @mainNodes[i].nameFra </div> <td> <td> <div class="mainCat mainCol"> @mainNodes[i].namePort </div> <td> <td> <div class="mainCat mainCol"> @mainNodes[i].nameRuss </div> <td class="column9">
内容的提问来源于stack exchange,提问作者inno
相关产品推荐
相关产品推荐

