目錄
【第一篇】ASP.NET MVC快速入門之?dāng)?shù)據(jù)庫操作(MVC5+EF6)
【第二篇】ASP.NET MVC快速入門之?dāng)?shù)據(jù)注解(MVC5+EF6)
【第三篇】ASP.NET MVC快速入門之安全策略(MVC5+EF6)
【第四篇】ASP.NET MVC快速入門之完整示例(MVC5+EF6)
一富蓄、完善數(shù)據(jù)注解
到目前為止的表格頁面效果:
我們需要更多的數(shù)據(jù)注解雹拄,來限制各個屬性,以及提供顯示用的名稱(而不是英文字符串):
public class Student
{
public int ID { get; set; }
[Display(Name = "姓名")]
[Required]
[StringLength(200, MinimumLength = 2)]
public string Name { get; set; }
[Display(Name = "性別")]
[Required]
[Range(0, 1)]
public int Gender { get; set; }
[Display(Name = "所學(xué)專業(yè)")]
[Required]
[StringLength(200)]
public string Major { get; set; }
[Display(Name = "入學(xué)日期")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EntranceDate { get; set; }
}
再次運行式镐,表格頁面效果:
完善性別的顯示,表格頁面-性別列顯示為中文,這個比較簡單艰额,將原來的:
@Html.DisplayFor(modelItem => item.Gender)
修改為:
@if (item.Gender == 1)
{
@:男
} else
{
@:女
}
新建編輯頁面-性別顯示為下拉列表
原來的編輯頁面:
性別字段的編輯框是通過如下方式生成的:
@Html.EditorFor(model => model.EntranceDate, new { htmlAttributes = new { @class = "form-control" } })
Html輔助方法EditorFor會查看模型屬性的類型,自動生成對應(yīng)的表單輸入框。由于性別字段是整形杭措,所以這里默認(rèn)會生成一個數(shù)字輸入框。
為了更加友好的顯示钾恢,我們將性別改為下拉列表瓤介,并且僅允許用戶從下拉項中選擇。首先我們需要準(zhǔn)備下拉列表選項的集合赘那,并通過控制器傳遞給視圖使用:
定義獲取性別集合的函數(shù)刑桑,由于需要多個地方使用,所以提取成一個公共方法:
private List<SelectListItem> GetGenderList()
{
return new List<SelectListItem>() {
new SelectListItem
{
Text = "男",
Value = "1"
},new SelectListItem
{
Text = "女",
Value = "0"
}
};
}
通過ViewBag.GenderList傳入視圖:
// GET: Students/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
ViewBag.GenderList = GetGenderList();
return View(student);
}
視圖中通過DropDownListFor強類型輔助方法募舟,來顯示下拉列表以及選中項:
@Html.DropDownListFor(model => model.Gender,
ViewBag.GenderList as IEnumerable<SelectListItem>, new { @class = "form-control" })
表單提交時的代碼和之前一樣祠斧,多了一個對GetGenderList的調(diào)用:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,Name,Gender,Major,EntranceDate")] Student student)
{
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenderList = GetGenderList();
return View(student);
}
這一點非常重要,雖然正常的提交操作不會再次返回當(dāng)前視圖(RedirectToAction直接指定了頁面跳轉(zhuǎn))拱礁,但是在模型綁定失敗時(嘗試禁用JavaScript琢锋,姓名留空,然后提交表單)呢灶,如果不重新設(shè)置ViewBag.GenderList參數(shù)就會出錯:
二吴超、表單檢索
下面我們?yōu)楸砀耥撁嬖黾右粋€搜索表單,用來對表格數(shù)據(jù)進行過濾鸯乃。
先增加一些記錄:
添加表單檢索字段:
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<p>
所學(xué)專業(yè): @Html.DropDownList("Major",
ViewBag.MajorList as IEnumerable<SelectListItem>, "全部")
姓名: @Html.TextBox("Name")
<input type="submit" value="檢索" />
</p>
}
由于本示例比較簡單鲸阻,沒有單獨的表來存儲所學(xué)專業(yè),因此我們需要從用戶表中檢索缨睡,并存儲到ViewBag.MajorList中傳入視圖:
private List<SelectListItem> GetMajorList()
{
var majors = db.Students.OrderBy(m => m.Major).Select(m => m.Major).Distinct();
var items = new List<SelectListItem>();
foreach(string major in majors)
{
items.Add(new SelectListItem {
Text = major,
Value = major
});
}
return items;
}
// GET: Students
public ActionResult Index()
{
ViewBag.MajorList = GetMajorList();
return View(db.Students.ToList());
}
頁面運行效果:
增加POST請求的處理方法:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(string Major, string Name)
{
var students = db.Students as IQueryable<Student>;
if (!String.IsNullOrEmpty(Name))
{
students = students.Where(m => m.Name.Contains(Name));
}
if (!String.IsNullOrEmpty(Major))
{
students = students.Where(m => m.Major == Major);
}
ViewBag.MajorList = GetMajorList();
return View(students.ToList());
}
此時的運行效果:
三鸟悴、數(shù)據(jù)庫分頁
分頁工具條
首先改造視圖代碼,增加分頁工具條:
<div id="pagebar">
@for (var i = 0; i < ViewBag.PageCount; i++)
{
if (i == ViewBag.PageIndex)
{
<span class="currentpagenumber">@(i + 1)</span>
}
else
{
<a class="pagenumber" href="javascript:;">@(i + 1)</a>
}
}
</div>
其中ViewBag.PageIndex和ViewBag.PageCount是由控制器傳入的分頁參數(shù)奖年,我們需要這兩個數(shù)據(jù)來構(gòu)造分頁鏈接细诸,如果是當(dāng)前分頁就顯示為文本,如果是其他頁就顯示為超鏈接陋守,然后通過客戶端JavaScript來注冊點擊事件震贵。
EF的數(shù)據(jù)庫分頁
后臺控制器代碼:
private static readonly int PAGE_SIZE = 3;
private int GetPageCount(int recordCount)
{
int pageCount = recordCount / PAGE_SIZE;
if (recordCount % PAGE_SIZE != 0)
{
pageCount += 1;
}
return pageCount;
}
private List<Student> GetPagedDataSource(IQueryable<Student> students,
int pageIndex, int recordCount)
{
var pageCount = GetPageCount(recordCount);
if (pageIndex >= pageCount && pageCount >= 1)
{
pageIndex = pageCount - 1;
}
return students.OrderBy(m => m.Name)
.Skip(pageIndex * PAGE_SIZE)
.Take(PAGE_SIZE).ToList();
}
// GET: Students
public ActionResult Index()
{
var students = db.Students as IQueryable<Student>;
var recordCount = students.Count();
var pageCount = GetPageCount(recordCount);
ViewBag.PageIndex = 0;
ViewBag.PageCount = pageCount;
ViewBag.MajorList = GetMajorList();
return View(GetPagedDataSource(students, 0, recordCount));
}
EF為我們封裝了大部分的細(xì)節(jié),所以上面的數(shù)據(jù)庫分頁代碼非常直觀和容易理解:
students
.OrderBy(m => m.Name)
.Skip(pageIndex * PAGE_SIZE)
.Take(PAGE_SIZE).ToList()
完成一個典型的數(shù)據(jù)庫分頁需要如下幾部:
OrderBy
:指定排序列Skip
:跳過多少條記錄Take
:返回的最大記錄數(shù)
上面的OrderBy是必須指定的水评,否則就會報錯:
分頁SQL語句
完成上面的代碼猩系,分頁效果已經(jīng)出來了:
下面,我們使用第三方Express Profiler工具來檢查EF生成的數(shù)據(jù)庫分頁SQL語句之碗。
首先下載工具:
http://expressprofiler.codeplex.com/
打開Express Profiler蝙眶,在Server文本框中輸入(LocalDb)\MSSQLLocalDB,如果你使用的VS2013,這個字符串可能是:(LocalDb)\v11.0幽纷,點擊綠色的啟用按鈕:
運行我們的示例式塌,轉(zhuǎn)到學(xué)生列表頁面,然后清空Express Profiler中的全部顯示友浸,再點擊第二頁:
可以看到這里有3次SQL查詢峰尝,這個和我們的心理預(yù)期是一樣的:
第一次SQL查詢:總記錄數(shù)
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Students] AS [Extent1]
) AS [GroupBy1]
go
對應(yīng)的C#代碼:
var students = db.Students as IQueryable<Student>;
var recordCount = students.Count();
- 第二次SQL查詢:所學(xué)專業(yè)集合(去除重復(fù))
SELECT
[Distinct1].[Major] AS [Major]
FROM ( SELECT DISTINCT
[Extent1].[Major] AS [Major]
FROM [dbo].[Students] AS [Extent1]
) AS [Distinct1]
go
對應(yīng)的C#代碼:
var majors = db.Students.OrderBy(m => m.Major).Select(m => m.Major).Distinct();
第三次SQL查詢:分頁數(shù)據(jù)
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[Name] AS [Name],
[Extent1].[Gender] AS [Gender],
[Extent1].[Major] AS [Major],
[Extent1].[EntranceDate] AS [EntranceDate],
[Extent1].[Job] AS [Job]
FROM [dbo].[Students] AS [Extent1]
ORDER BY [Extent1].[Name] ASC
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY
go
對應(yīng)的C#代碼:
return students.OrderBy(m => m.Name)
.Skip(pageIndex * PAGE_SIZE)
.Take(PAGE_SIZE).ToList();
這個查詢順序也和前面的EF代碼的執(zhí)行順序一模一樣,可以再回過頭看下控制器Index方法收恢。
同時處理表單檢索和數(shù)據(jù)庫分頁
不過目前遇到點難題武学,我們希望實現(xiàn)如下兩個功能:
點擊分頁鏈接時會發(fā)出HTTP POST請求,在請求參數(shù)中帶上表單檢索值伦意。
表單檢索時火窒,在請求參數(shù)中帶上當(dāng)前所在的分頁索引。
實現(xiàn)這兩個功能才算完善驮肉,否則表單檢索時如果丟失分頁參數(shù)熏矿,就會回到第一頁;而分頁時如果丟失表單參數(shù)离钝,就會清空表單輸入框票编。
是不是開始懷念WebForms了,在WebForms中整個頁面都被包含在一個表單中卵渴,因此回發(fā)時根本不需要考慮哪些參數(shù)后臺需要慧域。而MVC中這個就需要我們操心了,畢竟在靈活性的面前浪读,便利性就會有所打折昔榴。
我們采取的辦法是擴充前面的form標(biāo)簽,加入PageIndex隱藏字段瑟啃,然后點擊分頁鏈接時提交表單即可:
@using (Html.BeginForm("Index", "Students", FormMethod.Post, new { id = "searchForm" }))
{
@Html.AntiForgeryToken()
<p>
所學(xué)專業(yè): @Html.DropDownList("Major",
ViewBag.MajorList as IEnumerable<SelectListItem>, "全部")
姓名: @Html.TextBox("Name")
<input type="hidden" id="PageIndex" name="PageIndex" value="0" />
<input type="button" id="searchButton" value="檢索" />
</p>
}
注冊JavaScript腳本來處理點擊[檢索]按鈕和分頁鏈接:
@section scripts {
<script>
function submitForm(pagenumber) {
pagenumber = parseInt(pagenumber, 10);
$('#PageIndex').val(pagenumber - 1);
$('#searchForm').submit();
}
$(function () {
$('#searchButton').click(function () {
submitForm($('#pagebar .currentpagenumber').text());
});
$('#pagebar .pagenumber').click(function () {
submitForm($(this).text());
});
});
</script>
}
現(xiàn)在看下效果论泛,首先檢索所學(xué)專業(yè):
然后點擊第二頁,會發(fā)出一個POST請求:
可以看到本次請求蛹屿,上面的用戶輸入和PageIndex都發(fā)送到了控制器處理方法:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(string Major, string Name, int PageIndex)
{
var students = db.Students as IQueryable<Student>;
if (!String.IsNullOrEmpty(Name))
{
students = students.Where(m => m.Name.Contains(Name));
}
if (!String.IsNullOrEmpty(Major))
{
students = students.Where(m => m.Major == Major);
}
var recordCount = students.Count();
var pageCount = GetPageCount(recordCount);
if (PageIndex >= pageCount && pageCount >= 1)
{
PageIndex = pageCount - 1;
}
students = students.OrderBy(m=>m.Name)
.Skip(PageIndex * PAGE_SIZE).Take(PAGE_SIZE);
ViewBag.PageIndex = PageIndex;
ViewBag.PageCount = pageCount;
ViewBag.MajorList = GetMajorList();
return View(students.ToList());
}
這里需要注意一點:先進行表單過濾,然后執(zhí)行獲取總記錄數(shù)的查詢岩榆,最后再獲取分頁數(shù)據(jù)错负。這個順序不能變,因為表單過濾后總記錄才能確定下來勇边。
四犹撒、小結(jié)
本篇文章對示例進行了完善,首先是添加更多的數(shù)據(jù)注解粒褒,然后將顯示的性別由數(shù)字改為字符串识颊,對于編輯和新建頁面,將性別渲染為下拉列表。然后為表格頁面增加表單檢索功能祥款,可以根據(jù)[所學(xué)專業(yè)]和[姓名]對表格過濾清笨,最后完成了數(shù)據(jù)庫分頁功能。
本系列文章至此已經(jīng)完成了刃跛,下面我們簡單總結(jié)下用到的關(guān)鍵詞:路由引擎抠艾、控制器向視圖傳值、強類型輔助方法桨昙、模型綁定检号、數(shù)據(jù)注解、數(shù)據(jù)遷移蛙酪、客戶端驗證齐苛、服務(wù)器端模型驗證、模擬POST請求桂塞、表單身份驗證凹蜂、跨站請求偽造、過多提交攻擊藐俺、表單檢索炊甲、數(shù)據(jù)庫分頁。
文章轉(zhuǎn)載自:http://www.cnblogs.com/sanshi/p/6211164.html