MVC5+EF6 入門完整教程九

MiroYuan發表於2015-02-12

前一陣子臨時有事,這篇文章釋出間隔比較長,我們先回顧下之前的內容,每篇文章用一句話總結重點。

文章一 MVC核心概念簡介,一個基本MVC專案結構

文章二 通過開發一個最基本的登入介面,介紹瞭如何從Controller中獲取表單資料

文章三 EF的整個開發過程

文章四 EF基本的CRUD和常用的HtmlHelper

文章五 使用佈局頁(模板頁)改造UI

文章六 分部檢視(Partial View)

文章七 排序過濾分頁

文章八 不丟失資料進行資料庫結構升級

以上如果有不清楚的可以再回去看一下。

 

文章提綱

  • 理論基礎
  • 應用場景
  • 總結

理論基礎

基於前面的文章,本次我們更近一步,進行更加深入的講解, 首先介紹下Attribute配置Data Model.

使用Attribute配置Data Model, 可以指定formatting, validation, database mapping rules

約定:下圖中三種情況一般資料都翻譯成"屬性",為了區分,我們用下圖中的表述方式。

接下來,我們先對常用的attribute進行舉例說明。

一、常用Attribute

DataType,DisplayFormate

首先開啟ModelsàSysUser.cs

新增 public DateTime CreateDate { get; set; }

增加完之後及時使用Code First Migrations 方式更新資料庫。(不然執行時會報contex和database不一致的錯誤)

Note

注意把Migrations\ Configuration.cs中Seed方法中內容註釋掉,因為

模型變了,插入示例資料時會報一個錯誤。

執行 add-migration AddCreateDateToSysUserupdate-database即可更新

更新後隨便在資料庫中插入兩個日期值。

Code First Migrations 方式更新資料庫詳細做法參加上篇文章。

 

接著修改Views\Account\Index.cshtml,把建立日期顯示出來,如下方框處。

大家注意到,預設情況下會顯示出時間,我們只需要顯示到日就可以了。

下面我們就把CreateDate調整到我們需要的格式。

開啟 Models\SysUser.cs, 做如下修改。

DataType 屬性用來指定更加具體的資料型別,DataType列舉值提供了一些常見的型別,比如Date,Time,EmailAddress等。

但是DataType不能指定資料型別的顯示格式,例如日期要什麼格式顯示。

預設情況下顯示格式會根據電腦的設定顯示。

這個時候就需要配合使用DisplayFormate屬性來指定格式。

[DisplayFormat(DataFormatString="{0:yyyy-MM-dd}",ApplyFormatInEditMode=true)]

 

StringLength

你可以指定資料驗證規則以及出錯資訊。

StringLength屬性設定了資料庫中儲存欄位的最大長度,為程式提供客戶端和伺服器端的驗證。同樣用這個屬性也可以指定最小長度,不過不影響資料庫的結構。

同樣更新下資料庫

add-migration MaxLengthOnNames
update-database

先去資料庫看下,可以看到已經有長度限制了。

我們再修改下Create方法,測試下驗證。

之前我們的模型太簡陋了,為了看到效果,再做兩處修改。

Views\Account\Create.cshtml增加一個Helper:ValidationMessageFor用來顯示驗證資訊

Controllers\AccountController.cs增加一個判斷條件ModelState.IsValid,不然會出錯。

執行可以看到如下效果:

 

Column

這個屬性也非常實用。

有時會有這麼一種情況,我們Model中的欄位和資料庫中表的欄位要用不同的命名。例如我們Model中命名為UserName,資料庫表中命名為LoginName.

這個時候就用到Column了。

同樣執行更新指令。

add-migration ColumnLoginName

update-database

開啟資料庫可以看到UserName已經變成LoginName了。


下面再列出其他常用的attribute, 就不舉展開講了,很容易可以看懂,大家可以自己嘗試。

[Display(Name="使用者名稱")]

[StringLength(10,MinimumLength=1,ErrorMessage="名字在1和10個字之間")]

 

Note

1.可以將多個屬性寫在一塊用逗號隔開,例如

[Column("FirstName"),Display(Name = "First Name"),StringLength(50, MinimumLength=1)]

2.對某一些型別來說不需要使用Required, 例如DateTime, int,double,float,因為這些值型別不能被賦予空值,因此他們天生就具有Required的特性。

 [Column(TypeName="money")]

public
						decimal
								Budget { get; set; }
														

之前用Column可以改變資料庫中列名。

指定Column的TypeName可以改變SQL data type,這個例子中就是知道使用SQL Server的money型別。

Column mapping一般來說不需要,因為EF通常會基於你為property定義的CLR型別選擇合適的SQL Server data type.

The CLR decimal type maps to a SQL Server decimal type.

詳細對應表:https://msdn.microsoft.com/en-us/library/bb896344.aspx

 

二、Lazy, Eager, and Explicit Loading of Related Data

前面文章中我們介紹過顯示關聯表資料的方法。

第四篇文章介紹過通過navigation 屬性顯示關聯表資料。

本篇文章就係統的講解下多表關聯資料顯示的問題。

有三種方式EF可以載入關聯資料到一個實體的navigation屬性中,下面我就直接用MSDN上的截圖來說明。

Lazy loading

第一次讀取entity的時候不會載入。

當需要讀取navigation property的時候,相關的資料將會被自動讀取。

這種情況會導致多次查詢資料庫。

Eager loading

當讀取entity的時候,相關資料會被一起讀取。

一般來說這種方式會產生一個join query來獲取所有需要的資料。

通過Include方法來指定eager loading.

Explicit loading    

和lazy loading類似,除了需要在程式碼中明確指定需要獲取的關聯資料。

在讀取navigation property時explicit loading 不會自動發生,你需要手動載入相關資料。

通過獲取object state manager entry for entity,呼叫Collection.Load method for collections或者Reference.Load method for properties that hold a single entity.

一般來說,只有在關閉lazying loading的時候才會使用explicit loading

lazy loading 和 explicit loading都不立即獲取property values,它們也被稱作deferred loading.

Disable lazy loading before serialization

disable lazy loading的兩種方式:

1.對特定的navigation properties來說,省略property的virtual關鍵字就可以了

2.對所有navigation properties來說, 在context類中,建構函式中設定LazyingLoadingEnabled 為false即可。

this.Configuration.LazyLoadingEnabled = false;

 

 

 

 

應用場景

場景一:多對一關係,顯示使用者及相應的部門(* to 0 or 1)

新建一個entity: SysDepartment

我們約定,某個使用者只能歸屬於0個或1個部門。

即使用者和部門的關係為(* to 0 or 1)

原來的SysUser中新增一個如下兩個property

使用 code first migrations的方式更新下資料庫。可以看到新的表結構已經生成了。

去資料庫中SysDepartment新增兩筆資料。

去資料庫中SysUser修改使用者對應的department

先看下原來的Views\Account\Index.cshtml

我們原來是顯示SysUser主表內容,當點選Details時通過navigation property實現SysUseràSysUserRoleàSysRole多表間查詢。

現在我們增加一列Department, 讓這個表格能直接顯示SysUser主表及相應的Department內容。

我們使用Eager Loading的方式將Department的內容也載入進去,開啟Controllers\AccountController.cs, 在index修改一處地方:

修改對應的View

執行,可以看到Department中的內容已經被我們載入進來了。

這個就是第一種場景,多對一的情況。

下面我們再來看多對多的情況。

場景二:多對多關係

多對多關係可以拆解成一對多的關係,例如使用者和角色(* to *)可拆解成:

顯示使用者及相應的角色(1 to *)

顯示角色及相應的使用者(1 to *)

為了演示這個場景,我們新建一個ViewModel,將需要顯示的表都放進去。

建立相應的Controller和View

因為前面的文章已經將基本的用法都講過了,我這裡就直接貼出程式碼以及最終的展示結果,如果有不理解的部分再給我留言。

Controllers\UserRoleController.cs:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using MVCDemo.ViewModels;

using MVCDemo.DAL;

using MVCDemo.Models;

using System.Data.Entity;

 

 

namespace MVCDemo.Controllers

{

public class UserRoleController : Controller

{

private AccountContext db = new AccountContext();

//

// GET: /UserRole/

public ActionResult Index(int? id)

{

var viewModel = new UserRoleIndexData();

viewModel.SysUsers = db.SysUsers

.Include(u=>u.SysDepartment)

.Include(u => u.SysUserRoles.Select(ur => ur.SysRole))

.OrderBy(u => u.UserName);

 

if (id!=null)

{

ViewBag.UserID = id.Value;

viewModel.SysUserRoles = viewModel.SysUsers.Where(u => u.ID == id.Value).Single().SysUserRoles;

viewModel.SysRoles = (viewModel.SysUserRoles.Where(

ur => ur.SysUserID == id.Value)).Select(ur=>ur.SysRole);

 

}

return View(viewModel);

}

    }

}

 

Views\UserRole\Index.cshtml

@model MVCDemo.ViewModels.UserRoleIndexData

 

@{

ViewBag.Title = "Index";

Layout = "~/Views/Shared/_LayoutAdmin.cshtml";

}

 

<h2>UserRoles</h2>

 

<p>

@Html.ActionLink("Create New", "Create")

</p>

 

<table class="table table-striped">

<tr>

<th>

UserName

</th>

<th>

Email

</th>

<th>

CreateDate

</th>

<th>

Department

</th>

<th>

Roles

</th>

<th></th>

</tr>

@foreach (var item in Model.SysUsers)

{

string selectedRow = "";

if (item.ID==ViewBag.UserID)

{

selectedRow = "success";

}

<tr class="@selectedRow">

<td>

@Html.DisplayFor(modelItem => item.UserName)

</td>

<td>

@Html.DisplayFor(modelItem => item.Email)

</td>

<td>

@Html.DisplayFor(modelItem => item.CreateDate)

</td>

<td>

@if (item.SysDepartment != null)

{

@item.SysDepartment.DepartmentName

}

</td>

<td>

@{

foreach (var userRole in item.SysUserRoles)

{

@userRole.SysRole.RoleName <br />

}

}

</td>

<td>

@Html.ActionLink("Select", "Index", new { id = item.ID })

 

</td>

</tr>

}

</table>

 

@if (Model.SysRoles != null)

{

<h3>Related Roles</h3>

<table class="table table-striped">

<tr>

<th>RoleName</th>

<th>RoleDesc</th>

</tr>

 

@foreach (var item in Model.SysRoles)

{

<tr>

<td>

@item.RoleName

</td>

<td>

@item.RoleDesc

</td>

</tr>

}

</table>

}

 

 

最終展示結果:

總結

一、掌握常用attribute

DataType

例子:[DataType(DataType.Date)]

DisplayFormat

例子:

[DisplayFormat(DataFormatString="{0:yyyy-MM-dd}",ApplyFormatInEditMode=true)]

[DisplayFormat(NullDisplayText = "No grade")]

StringLength

例子:

[StringLength(10,MinimumLength=1,ErrorMessage="名字在1和10個字之間")]

Column

例子:

[Column("FirstName")]

[Column(TypeName="money")]

Display

例子:

[Display(Name="使用者名稱")]

 

二、掌握載入多表資料兩種應用場景

 

好了,到目前為止,使用MVC+EF開發的基本知識差不多都齊全了。

在下一課,也就是我們第一階段的最後一課:MVC5+EF6入門完整教程十,我們將再講解下資料更新的部分,以及如何使用原生SQL,如何呼叫儲存過程等。

祝大家新年快樂,學習進步!

相關文章