ASP.NET MVC5+EF6(入門四完整示例)

目錄

【第一篇】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ù)庫分頁需要如下幾部:

  1. OrderBy:指定排序列

  2. Skip:跳過多少條記錄

  3. 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ù)期是一樣的:

  1. 第一次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();
  1. 第二次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();
  1. 第三次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)如下兩個功能:

  1. 點擊分頁鏈接時會發(fā)出HTTP POST請求,在請求參數(shù)中帶上表單檢索值伦意。

  2. 表單檢索時火窒,在請求參數(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欲芹,一起剝皮案震驚了整個濱河市卿啡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菱父,老刑警劉巖颈娜,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浙宜,居然都是意外死亡官辽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門粟瞬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來同仆,“玉大人,你說我怎么就攤上這事裙品∷着” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵市怎,是天一觀的道長岁忘。 經(jīng)常有香客問我,道長区匠,這世上最難降的妖魔是什么干像? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上麻汰,老公的妹妹穿的比我還像新娘速客。我一直安慰自己,他們只是感情好什乙,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布挽封。 她就那樣靜靜地躺著,像睡著了一般臣镣。 火紅的嫁衣襯著肌膚如雪辅愿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天忆某,我揣著相機與錄音点待,去河邊找鬼。 笑死弃舒,一個胖子當(dāng)著我的面吹牛癞埠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播聋呢,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼苗踪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了削锰?” 一聲冷哼從身側(cè)響起通铲,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎器贩,沒想到半個月后颅夺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡蛹稍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年吧黄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唆姐。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡拗慨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奉芦,到底是詐尸還是另有隱情胆描,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布仗阅,位于F島的核電站,受9級特大地震影響国夜,放射性物質(zhì)發(fā)生泄漏减噪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望筹裕。 院中可真熱鬧醋闭,春花似錦、人聲如沸朝卒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抗斤。三九已至囚企,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瑞眼,已是汗流浹背龙宏。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伤疙,地道東北人银酗。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像徒像,于是被迫代替她去往敵國和親黍特。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容