我有一个 SQL Server 数据库,并使用 ASP.NET Core 8.0 MVC 应用程序和 Entity Framework Core。我想调用一个存储过程,在控制器操作中按页码和每页结果数对其结果集进行分页。
我认为视图可以使用不同的页码一遍又一遍地调用控制器操作方法,但是当视图第二次调用下一个操作时(我可以成功调用一次),模型的页码为 1 -即使我在页面上显示它并且它显示 2.
我实际上正在调用 Next、Back、First 和 Last 的操作方法,分别减少、增加或更改当前页码,然后调用搜索控制器操作方法,并为新的当前页面更新模型。我也不确定这是否是最佳实践。
我第一次调用 Next 时,页码更新为 2,存储过程再次运行并检索正确的数据。第二次调用 Next 时,当前页面作为 1 传递到模型上,即使它显示在屏幕上并显示 2。
这是适用的代码。任何帮助将不胜感激。
CREATE OR ALTER PROCEDURE [dbo].[SEARCH]
...
@PageNumber int = 1,
@PageSize int = 20
SELECT ...(columns)...
FROM TABLE
WHERE
...
OFFSET ((@PageNumber - 1) * @PageSize) ROWS
FETCH NEXT @PageSize ROWS ONLY
END
查看:
<form id="frmSearch" method="post" asp-asp-controller="Home" asp-action="Search">
... 最初调用搜索的按钮:
<button id="submitSearch" type="submit" name="Submit" class="btn btn-lg btn-primary">Search</button>
下一个呼叫按钮:
<input id="btnNext" type="submit" value="Next" asp-action="Next" />
控制器:
public async Task<IActionResult> Next(SearchViewModel svm)
{
svm.CurrentPageNumber += 1;
return await Search(svm);
}
搜索(帖子):
[HttpPost]
public async Task<IActionResult> Search(SearchViewModel svm)
{
try
{
// ...
var parameters = new[]
{
new SqlParameter("@PageNumber", svm.CurrentPageNumber),
new SqlParameter("@PageSize", pagesize)
};
List<CommunicationSearchResult> communications = await _recordingContext.CommunicationSearchResults.FromSqlRaw("EXEC USP_DETAILED_SEARCH_COMMUNICATIONS @PageNumber, @PageSize", parameters).ToListAsync();
svm.Results = communications;
if (communications.Count > 0)
{
svm.TotalRecordCount = communications[0].Total;
svm.TotalPageCount = Convert.ToInt32(Math.Ceiling((decimal)(svm.TotalRecordCount / pagesize)));
}
return View("Search", svm);
}
catch()
{ }
}
当谈到 ASP.Net 时,一个重要的细节是,在服务器和视图之间来回传递对象的任何外观都是一种幻觉,与 MVVM /w WPF 等中的视图模型不同。为了让“模型”在传回控制器时传回除默认状态之外的任何内容,您需要在页面上有一个与该值相关的输入元素。
如果您的视图模型具有要在服务器端和/或客户端调整的 CurrentPageNumber,那么您可能会发现代码通过向页面添加该值的隐藏输入开始工作。例如:
@Html.HiddenFor(model => model.CurrentPageNumber)
当您将模型传递给视图时,您正在将该类的实例发送到 Web 服务器上的视图引擎。该视图引擎将 .cshtml 模板和模型转换为 .html 视图,以及管理表单回发等事务的 ASP.Net JavaScript。当用户发布表单或获取 FormData 以发送到服务器的 Ajax 调用时,客户端上没有“Model”实例。 JS 只是尝试解析 .html 页面上的所有输入值,以将这些值发送到服务器 POST 中。当您的控制器路由被调用时,ASP.Net 将遍历这些输入并构造一个新的 ViewModel 实例,填充传递的任何值。任何未传递的值都将保留为默认值。
拥有诸如
<p>
之类的元素内容或带有 @Model.CurrentPageNumber
的标签将在加载更新的页面时显示更新的第 2 页,但是如果没有 <input>
绑定到 @Model.CurrentPageNumber
,则更新的值将不会通过后续帖子。
分页的替代方法是在 JS 中完全在客户端跟踪当前页码,并使用 POST 有效负载传递当前页号(以及页面大小等信息),而不是在每次调用时发送视图模型。 当前页码也可以使用会话状态在服务器端进行跟踪,尽管我并不真正推荐这种存储瞬态状态的方法。 (很容易不同步,并且大量使用会话状态会占用服务器端内存)