MVC系列2-Model

上一篇我講了ASP.MET MVC的基礎概念,我相信從上一篇,咱們能夠知道MVC的執行過程。這一篇咱們開始講解Model。咱們知道,在咱們的應用程序中,大多時候是在遵循業務邏輯經過UI操做數據。因此這裏按照咱們上一篇講的分離關注點的觀點。咱們至少能夠把程序分爲三部分,UI,邏輯和數據。業務邏輯由咱們根據具體的領域來實現,UI其實從根本就是展示數據,收集數據。咱們的業務邏輯,操做的其實也仍是數據。因此這一篇,咱們從數據開始,由於我我的以爲,數據是最基本的。html

什麼是模型sql

  其實咱們從ASP.NET三層時代就開始接觸模型的概念,只是那個時候咱們一般稱之爲實體,這裏咱們簡單的談一下實體的概念。在談到現代軟件開發的時候,咱們聽的不少的一個詞就是面向對象,簡單點理解,就是程序模擬現實中或者虛擬物體的數據和行爲來達到完成現實中的既定任務。舉個例子來講,咱們人。在現實中,人會有一些特徵,好比身高:170cm,年齡:18。會說話,會走路,會跳,會跑。那麼在面向對象的應用程序中,咱們能夠定義一類物體所能具備的特徵。而後根據定義的這些特徵去產生具體的物體。就像餅乾模具和餅乾的關係。餅乾模具是定義了咱們能生產出什麼形狀的餅乾。咱們也能夠經過餅乾模具生產出特定於模具的餅乾。在程序中,就是類和對象。類是定義了對象能包含什麼樣的數據和行爲。而對象則是實際包含這些數據和行爲。而不一樣的領域所關注的特性是不同的。人這個物體,在銀行領域可能會關注你的信譽,存款等等特性。而在公司員工管理領域,可能關心的是你的年齡,技術方向等等。因此面對不一樣的領域,咱們須要抽象出不一樣的類型特徵。簡單歸納,模型就是面對特定領域抽象出的具備某些領域特定的對象。咱們能夠增長對象,修改對象,刪除對象,查看對象,最後持久化對象。說到底,咱們的對象是包含數據的,因此,咱們操做的也就是數據。數據庫

Model數據結構

  在ASP.NET中有兩種模型,一種是對象領域的,一種是面向視圖的。面向領域即數據根據特定的業務邏輯建模,面向視圖則是根據UI須要的數據來建模。這兩種模式各有好壞。咱們能夠混合使用,後面咱們會介紹到。app

  這裏,咱們經過一個功能來詳細講解Model在MVC中怎麼使用。咱們要實現的功能是用戶登陸,登錄以後用戶能夠看到一些新聞和產品在首頁。這裏根據咱們的業務邏輯,咱們至少須要三個模型:用戶,新聞,產品。咱們建立一個MVC項目。具體步驟見上一篇MVC基礎。接着,咱們在Model文件夾中添加如下三個類。ide

UserInfosqlserver

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

NewsInfo佈局

複製代碼
 1 public class NewsInfo
 2     {
 3         private int _newsInfoId;
 4 
 5         public virtual int NewsInfoId
 6         {
 7             get { return _newsInfoId; }
 8             set { _newsInfoId = value; }
 9         }
10 
11         private string _newsTitle;
12 
13         public virtual string NewsTitle
14         {
15             get { return _newsTitle; }
16             set { _newsTitle = value; }
17         }
18 
19         private string _newsContent;
20 
21         public virtual string NewsContent
22         {
23             get { return _newsContent; }
24             set { _newsContent = value; }
25         }
26 
27         private DateTime _createTime;
28 
29         public virtual DateTime CreateTime
30         {
31             get { return _createTime; }
32             set { _createTime = value; }
33         }
34     }
複製代碼

ProductInfopost

複製代碼
 1 public class ProductInfo
 2     {
 3         private int _productInfoId;
 4 
 5         public int ProductInfoId
 6         {
 7             get { return _productInfoId; }
 8             set { _productInfoId = value; }
 9         }
10 
11         private string _productName;
12 
13         public string ProductName
14         {
15             get { return _productName; }
16             set { _productName = value; }
17         }
18 
19         private string _productDescription;
20 
21         public string ProductDescription
22         {
23             get { return _productDescription; }
24             set { _productDescription = value; }
25         }
26 
27         private DateTime _createTime;
28 
29         public DateTime CreateTime
30         {
31             get { return _createTime; }
32             set { _createTime = value; }
33         }
34     }
複製代碼

當咱們建好了模型,如今就須要考慮怎麼操做這些模型。通常來講咱們有以下幾種方式網站

  • 數據庫,使用傳統的ADO.NET操做數據。
  • EF
  • 服務調用

   咱們這裏不介紹第一種方式,咱們使用第二,三種方式來結合model操做數據。這裏咱們額外介紹一個概念叫作基架,基價能夠爲咱們的程序生成樣板代碼,好比增長,修改,刪除,查找,基架會按照咱們定義的模板生成代碼,固然,這個模板咱們是能夠修改的。這裏,咱們就先使用基架來完成咱們上面的第二種方式,使用EF。

   使用基架的方式是咱們添加一個控制器,咱們能夠看到模板項,在模板項裏面有以下幾個選項

  • 空MVC控制器:這裏會在controller裏面幫咱們生成一個Index操做。
  • 包含讀寫操做和視圖的MVC(使用EF):這裏會幫咱們生成Index,Details,Create,Edit,Delete操做,而且生成視圖。並且還會幫咱們生成數據操做的代碼
  • 包含讀寫操做:幫咱們生成Index,Details,Create,Edit,Delete操做,可是沒有實際的代碼,不會生成視圖。

咱們使用第二種來生成數據操做代碼。

  這裏我就不過多的概述EF的使用方式,由於咱們這裏主要的思想是操做數據的方式,而不是特定於一種詳細的數據操做方式。若是對EF有興趣,請參考園子裏EF的文章。第一步,咱們須要一個數據上下文。所謂的數據上下文,其實就是一個數據的進口與入口。咱們經過這個上下文來操做數據。由於這裏咱們使用的是EF4.1.這個版本的EF是隨MVC3一塊兒發佈的。因此,這裏咱們新建一個數據上下文,新建數據上下文的方式是繼承一個名爲DbContext的類。

1     public class WebSiteContext:DbContext
2     {
3         public DbSet<UserInfo> Users { get; set; }
4         public DbSet<NewsInfo> News { get; set; }
5         public DbSet<ProductInfo> Products { get; set; }
6     }

  這個類很簡單,咱們聲明瞭3個類型爲DbSet的屬性。咱們來看看這個類型的解釋

按 Ctrl+C 複製代碼
按 Ctrl+C 複製代碼

其實摘要裏面已經說得很清楚,執行增,刪,改,查詢就是獲得這個集合。這裏順便多提一個概念,就是EF的使用方式

  • 數據優先:數據優先的方式是指,咱們程序的數據從數據庫開始,也就是說,數據庫的設計是第一位。咱們在程序裏使用數據裏的數據結構生成實體模型
  • Model優先:Model優先是指程序的數據模型從EF的Model設計開始。最後使用Model的數據結構生成數據庫的數據結構,這種結構不一一對應的,咱們使用Mapping創建模型屬性與數據庫字段的關係
  • 代碼優先:代碼優先是指能夠在不建立數據庫,也不建立EF Model的狀況下,編寫純C#類。由於EF可以理解咱們的類,並根據咱們的配置串在正確的位置幫咱們生成數據存儲實例。

  這裏咱們使用的就是代碼優先,另外還有一點咱們須要注意,就是代碼優先約定。咱們前面提到過這個概念,MVC遵循了許多約定優先於配置的概念,這裏也是同樣,假設咱們有一個UseInfo類,那麼EF就會假設把數據存儲到在數據庫中一個名爲UserInfo的表中。若是要存儲的對象有一個名爲ID的屬性,那麼EF就假這個值爲主鍵值,並把這個值賦給sqlserver中對應的自動遞增標識列。這裏是EF的概念,若是想了解更多,請參考其餘關於EF的文章。好了,前面咱們已經建立了咱們的數據上下文。如今咱們來應用這個數據上下文生成咱們的控制。由於前面咱們已經完成了必要的前期工做,完成了咱們的Model,完成了咱們的數據操做方法(數據上下文)。下面咱們就開始完成咱們的Controller。

這裏咱們使用了基架是包含讀寫和視圖,並使用EF。咱們爲咱們的Controller選定一個模型類,這裏咱們新建的是一個Login控制器,因此,模型是咱們的UserInfo。數據上下文爲咱們剛剛新建的數據上下文。須要注意的一點是,這裏是徹底限定名。即咱們須要加上命名空間。在高級選項裏有一些關於View的設置,好比自動添加腳本引用,是否引用佈局。這裏根據我的程序的設定。點擊添加就完成了咱們Controller的建立,並且,咱們能夠看到,咱們不只建立了Controller,而且,VS還幫助咱們建立了對應的View和Action。

  咱們實際上是能夠刪除生成的View,由於在不少時候,VS幫咱們生成的View是不符合咱們本身程序的要求。因此,這裏咱們刪除Login下的全部的View。固然,除了Index View。由於這個View是咱們須要使用的。咱們再來看看Controller的代碼。

複製代碼
 1 public class LoginController : Controller
 2     {
 3         private WebSiteContext db = new WebSiteContext();
 4 
 5         //
 6         // GET: /Login/
 7 
 8         public ActionResult Index()
 9         {
10             return View(db.Users.ToList());
11         }
12 
13         public ActionResult Details(int id = 0)
14         {
15             UserInfo userinfo = db.Users.Find(id);
16             if (userinfo == null)
17             {
18                 return HttpNotFound();
19             }
20             return View(userinfo);
21         }
22 
23         public ActionResult Create()
24         {
25             return View();
26         }
27 
28         [HttpPost]
29         public ActionResult Create(UserInfo userinfo)
30         {
31             if (ModelState.IsValid)
32             {
33                 db.Users.Add(userinfo);
34                 db.SaveChanges();
35                 return RedirectToAction("Index");
36             }
37 
38             return View(userinfo);
39         }
40 
41         public ActionResult Edit(int id = 0)
42         {
43             UserInfo userinfo = db.Users.Find(id);
44             if (userinfo == null)
45             {
46                 return HttpNotFound();
47             }
48             return View(userinfo);
49         }
50 
51         [HttpPost]
52         public ActionResult Edit(UserInfo userinfo)
53         {
54             if (ModelState.IsValid)
55             {
56                 db.Entry(userinfo).State = EntityState.Modified;
57                 db.SaveChanges();
58                 return RedirectToAction("Index");
59             }
60             return View(userinfo);
61         }
62 
63         public ActionResult Delete(int id = 0)
64         {
65             UserInfo userinfo = db.Users.Find(id);
66             if (userinfo == null)
67             {
68                 return HttpNotFound();
69             }
70             return View(userinfo);
71         }
72 
73         [HttpPost, ActionName("Delete")]
74         public ActionResult DeleteConfirmed(int id)
75         {
76             UserInfo userinfo = db.Users.Find(id);
77             db.Users.Remove(userinfo);
78             db.SaveChanges();
79             return RedirectToAction("Index");
80         }
81 
82         protected override void Dispose(bool disposing)
83         {
84             db.Dispose();
85             base.Dispose(disposing);
86         }
87     }
複製代碼

  這裏幫咱們生成了對應的增,刪,改,查的方法。在Login操做中,咱們是不須要增長,刪除,和修改的。咱們須要的是經過用戶名,密碼,和登錄狀態來驗證用戶的登錄。因此,這裏咱們刪除沒必要要的代碼而後增長咱們須要的方法。

複製代碼
 1 public class LoginController : Controller
 2     {
 3         private WebSiteContext db = new WebSiteContext();
 4 
 5         public ActionResult Index()
 6         {
 7             return View();
 8         }
 9 
10         [HttpPost]
11         public ActionResult Login(UserInfo userInfo)
12         {
13             if (ModelState.IsValid)
14             {
15                 UserInfo user = db.Users.FirstOrDefault(u=>u.UserName==userInfo.UserName&&u.Password==userInfo.Password);
16                 if (user!=null)
17                 {
18                     if (user.IsLogin == false)
19                     {
20                         return RedirectToAction("Index","Home");
21                     }
22                     ModelState.AddModelError("","用戶已經登錄");
23                     return View("Index",userInfo);
24                 }
25             }
26             ModelState.AddModelError("", "該用戶沒有註冊,請註冊用戶");
27             return View("Index", userInfo);
28         }
29 
30         protected override void Dispose(bool disposing)
31         {
32             db.Dispose();
33             base.Dispose(disposing);
34         }
35     }
複製代碼

  接着,咱們須要替換咱們的IndexView,由於。咱們這裏須要的方法是登錄,而默認基架生成的代碼並無給咱們提供這個方法。因此,這裏咱們須要本身定義咱們的Index View

複製代碼
 1 @model ModelInMVC.Models.UserInfo
 2 
 3 @{
 4     ViewBag.Title = "Login";
 5 }
 6 
 7 <h2>Login</h2>
 8 @using(Html.BeginForm("Login","Login",FormMethod.Post))
 9 {
10     @Html.ValidationSummary(true)
11 <fieldset>
12     <legend>UserInfo</legend>
13     @Html.LabelFor(m=>m.UserName)
14     @Html.TextBoxFor(m=>m.UserName)
15 
16     @Html.LabelFor(m => m.Password)
17     @Html.PasswordFor(m=>m.Password)
18 
19     <input type="submit" value="登錄" />
20 </fieldset>
21 }
複製代碼

  而後,咱們須要更改咱們的路由設置,把默認頁改成咱們的登錄頁

複製代碼
 1 public class RouteConfig
 2     {
 3         public static void RegisterRoutes(RouteCollection routes)
 4         {
 5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 6 
 7             routes.MapRoute(
 8                 name: "Default",
 9                 url: "{controller}/{action}/{id}",
10                 defaults: new { controller = "Login", action = "Index", id = UrlParameter.Optional }
11             );
12         }
13     }
複製代碼

  添加一個HomeController,也就是咱們的主頁的控制器

複製代碼
 1     public class HomeController : Controller
 2     {
 3         //
 4         // GET: /Home/
 5 
 6         public ActionResult Index()
 7         {
 8             return View();
 9         }
10 
11     }
複製代碼

  代碼很簡單,就只有一個Index Action,接着添加一個Home下的Index View。

1 @{
2     ViewBag.Title = "Index";
3     Layout = "~/Views/Shared/_Layout.cshtml";
4 }
5 
6 <h2>HomeIndex</h2>

  一樣也是很簡單的,由於,咱們所要作的,僅僅是登錄成功的一個跳轉,固然,後續裏面還會有別的操做。這個時候,其實就能夠運行咱們的程序了,可是這是沒有意義的,由於咱們沒有任何的數據。因此,下面咱們來給咱們的程序添加數據。

初始化數據

咱們可使用EF的數據初始化策略初始化咱們程序須要的數據,首先,在config文件中,有咱們的鏈接字符串。

1     <add name="WebSiteContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=WebSiteContext-20131204232726; Integrated Security=True; MultipleActiveResultSets=True; AttachDbFilename=|DataDirectory|WebSiteContext-20131204232726.mdf"
2       providerName="System.Data.SqlClient" />

  這裏我使用的是本地數據庫。你們能夠根據本身的配置去完成數據庫的配置,若是是本地數據庫,會在appData中生成對應的數據庫文件。在EF中,咱們可使用Database.SetInitializer方法初始化咱們的數據庫

複製代碼
        //
        // 摘要: 
        //     獲取或設置數據庫初始化策略。在從 System.Data.Entity.Infrastructure.DbCompiledModel 初始化 System.Data.Entity.DbContext
        //     實例時,調用數據庫初始化策略。
        //
        // 參數: 
        //   strategy:
        //     策略。
        //
        // 類型參數: 
        //   TContext:
        //     上下文的類型。
複製代碼

 咱們能夠看到參數爲IDatabaseInitializer<TContext>。EF中兩個類實現了這個接口

  • DropCreateDatabaseIfModelChanges:當模型發生變化的時候纔會從新建立數據庫
  • DropCreateDatabaseAlways:每次從新啓動的時候都會從新建立數據庫

這裏我使用的是DropCreateDatabaseIfModelChanges,泛型類型爲咱們的Context

複製代碼
 1 public class WebSiteDbInitializer:DropCreateDatabaseIfModelChanges<WebSiteContext>
 2     {
 3         protected override void Seed(WebSiteContext context)
 4         {
 5             context.Users.Add(new UserInfo() { UserInfoId=0, UserName="admin", Password="admin", IsLogin=false });
 6             context.Users.Add(new UserInfo() { UserInfoId=1,UserName="edrick", Password="123", IsLogin=true});
 7 
 8             context.News.Add(new NewsInfo() { NewsInfoId = 0, NewsTitle = "今天霧霾", NewsContent = "今天霧霾", CreateTime = DateTime.Now });
 9             context.News.Add(new NewsInfo() { NewsInfoId = 1, NewsTitle = "今天霧霾很嚴重", NewsContent = "今天霧霾很嚴重", CreateTime = DateTime.Now });
10             context.News.Add(new NewsInfo() { NewsInfoId = 2, NewsTitle = "今天霧霾特別嚴重", NewsContent = "今天霧霾特別嚴重", CreateTime = DateTime.Now });
11 
12             context.Products.Add(new ProductInfo() { ProductInfoId = 0, ProductName = "霧霾口罩", ProductDescription = "霧霾口罩", CreateTime = DateTime.Now });
13             context.Products.Add(new ProductInfo() { ProductInfoId = 1, ProductName = "口罩", ProductDescription = "口罩", CreateTime = DateTime.Now });
14             context.Products.Add(new ProductInfo() { ProductInfoId = 2, ProductName = "醫用口罩", ProductDescription = "醫用口罩", CreateTime = DateTime.Now });
15             base.Seed(context);
16         }
17     }
複製代碼

  重寫基類的Seed方法能夠將新對象保存到數據庫中。而後咱們須要在應用程序啓動的時候註冊Database.SetInitializer方法

複製代碼
 1     public class MvcApplication : System.Web.HttpApplication
 2     {
 3         protected void Application_Start()
 4         {
 5             Database.SetInitializer(new WebSiteDbInitializer());
 6             AreaRegistration.RegisterAllAreas();
 7 
 8             WebApiConfig.Register(GlobalConfiguration.Configuration);
 9             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
10             RouteConfig.RegisterRoutes(RouteTable.Routes);
11             BundleConfig.RegisterBundles(BundleTable.Bundles);
12         }
13     }
複製代碼

  這樣咱們就完成了數據庫的設置與數據的初始化。好了,咱們能夠運行咱們的應用程序了。到這裏,咱們完成了咱們的登錄。可是,前面咱們說了,咱們要完成的是,登錄以後能夠看到新聞和產品。有了上面的基礎咱們能夠很快的建立新聞和產品的增刪改操做了。
  咱們開始建立咱們的新聞Controller。跟上面的步驟同樣,基架選擇讀寫/視圖和EF。這裏之因此使用基架,是由於它能幫咱們生成大部分咱們須要的方法和前段標籤。

接着,咱們新建產品Controller

所有完成以後,咱們會看到咱們的新聞和產品都有了對應Controller和View。如今咱們要作的就是更改這些代碼。由於前面咱們說了,咱們須要的只是基架幫咱們生成的一部分代碼,可是它並不符合咱們的要求。這裏咱們的要求就是咱們在主頁上要能看到新聞和產品,因此咱們須要把這兩個頁面的主頁合併到Home主頁裏面。這裏就有了一個新的問題,home主頁的數據從哪裏來。從第一篇,MVC基礎中咱們能夠知道。我能夠把數據封裝到Viewbag中,而後傳遞給View,固然,這是一種解決辦法。這裏介紹另一種解決辦法,ViewModel。

ViewModel

  這裏須要注意一點的事,這裏的ViewModel不等於MVVM中的ViewModel。這裏的ViewModel指的是咱們爲特定的頁面制定頁面模型。好比通常的門戶網站的首頁,咱們須要顯示少許的新聞,少許的案例,少許的產品的信息。可是這些數據又都是不一樣的模型。因此,咱們就能夠給這個頁面創建一個特定於這個特面的模型。這裏咱們就是IndexViewModel。它包含的就是新聞和產品的集合。它給Index頁面提供數據。

複製代碼
 1 public class IndexViewModel
 2     {
 3         public NewsInfo News { get; set; }
 4 
 5         public ProductInfo Product { get;set;}
 6 
 7         public IEnumerable<NewsInfo> NewsInfoList { get; set; }
 8 
 9         public IEnumerable<ProductInfo> ProductInfoList { get; set; }
10     }
複製代碼

有了數據,咱們就須要整合咱們剛剛VS幫咱們生成的代碼,咱們須要把News Index的代碼和Product Index的代碼整合到Home Index下。可是,直接複製代碼是運行不了的,由於新聞和產品的Index綁定的模型的是IEnumerable<ModelInMVC.Models.NewsInfo>和IEnumerable<ModelInMVC.Models.ProductInfo>而咱們的Home Index是沒有綁定這兩個模型。因此咱們就要使用咱們上面定義的ViewModel。讓IndexViewModel綁定到Index。修改HomeController的Index方法。

複製代碼
 1     public class HomeController : Controller
 2     {
 3         WebSiteContext context = new WebSiteContext();
 4         //
 5         // GET: /Home/
 6 
 7         public ActionResult Index()
 8         {
 9             IndexViewModel viewModel = new IndexViewModel();
10             viewModel.NewsInfoList = context.News.ToList();
11             viewModel.ProductInfoList = context.Products.ToList();
12             return View(viewModel);
13         }
14     }
複製代碼

 好了,數據準備好了,咱們如今把Home Index修改成以下代碼

複製代碼
 1 @model ModelInMVC.Models.IndexViewModel
 2 @{
 3     ViewBag.Title = "Index";
 4     Layout = "~/Views/Shared/_Layout.cshtml";
 5 }
 6 
 7 <h2>HomeIndex</h2>
 8 <div>
 9     <div style="width:49%;float:left;">
10         <fieldset>
11             <legend>News</legend>
12             <p>
13                 @Html.ActionLink("Create New", "Create")
14             </p>
15             <table>
16                 <tr>
17                     <th>
18                         @Html.DisplayNameFor(model => model.News.NewsTitle)
19                     </th>
20                     <th>
21                         @Html.DisplayNameFor(model => model.News.NewsContent)
22                     </th>
23                     <th>
24                         @Html.DisplayNameFor(model => model.News.CreateTime)
25                     </th>
26                     <th></th>
27                 </tr>
28 
29                 @foreach (var item in Model.NewsInfoList)
30                 {
31                 <tr>
32                     <td>
33                         @Html.DisplayFor(modelItem => item.NewsTitle)
34                     </td>
35                     <td>
36                         @Html.DisplayFor(modelItem => item.NewsContent)
37                     </td>
38                     <td>
39                         @Html.DisplayFor(modelItem => item.CreateTime)
40                     </td>
41                     <td>
42                         @Html.ActionLink("Edit", "Edit", new { id = item.NewsInfoId }) |
43                         @Html.ActionLink("Details", "Details", new { id = item.NewsInfoId }) |
44                         @Html.ActionLink("Delete", "Delete", new { id = item.NewsInfoId })
45                     </td>
46                 </tr>
47                 }
48             </table>
49         </fieldset>
50     </div>
51     <div style="width:49%;float:right">
52         <fieldset>
53             <legend>Product</legend>
54             <p>
55                 @Html.ActionLink("Create New", "Create")
56             </p>
57             <table>
58                 <tr>
59                     <th>
60                         @Html.DisplayNameFor(model => model.Product.ProductName)
61                     </th>
62                     <th>
63                         @Html.DisplayNameFor(model => model.Product.ProductDescription)
64                     </th>
65                     <th>
66                         @Html.DisplayNameFor(model => model.Product.CreateTime)
67                     </th>
68                     <th></th>
69                 </tr>
70 
71                 @foreach (var item in Model.ProductInfoList)
72                 {
73                     <tr>
74                         <td>
75                             @Html.DisplayFor(modelItem => item.ProductName)
76                         </td>
77                         <td>
78                             @Html.DisplayFor(modelItem => item.ProductDescription)
79                         </td>
80                         <td>
81                             @Html.DisplayFor(modelItem => item.CreateTime)
82                         </td>
83                         <td>
84                             @Html.ActionLink("Edit", "Edit", new { id = item.ProductInfoId }) |
85                             @Html.ActionLink("Details", "Details", new { id = item.ProductInfoId }) |
86                             @Html.ActionLink("Delete", "Delete", new { id = item.ProductInfoId })
87                         </td>
88                     </tr>
89                 }
90             </table>
91         </fieldset>
92     </div>
93 </div>
複製代碼

 運行程序,咱們能夠看到以下的顯示

當咱們點擊增長,修改,詳細,刪除的時候出現404.由於基架生成的Controller路由不符合咱們修改後的路徑。因此,下一步,咱們要修改頁面的連接和Contrller的部分代碼。

修改以後的頁面代碼

複製代碼
 1 @model ModelInMVC.Models.IndexViewModel
 2 @{
 3     ViewBag.Title = "Index";
 4     Layout = "~/Views/Shared/_Layout.cshtml";
 5 }
 6 
 7 <h2>HomeIndex</h2>
 8 <div>
 9     <div style="width:49%;float:left;">
10         <fieldset>
11             <legend>News</legend>
12             <p>
13                 @Html.ActionLink("Create New", "Create","News")
14             </p>
15             <table>
16                 <tr>
17                     <th>
18                         @Html.DisplayNameFor(model => model.News.NewsTitle)
19                     </th>
20                     <th>
21                         @Html.DisplayNameFor(model => model.News.NewsContent)
22                     </th>
23                     <th>
24                         @Html.DisplayNameFor(model => model.News.CreateTime)
25                     </th>
26                     <th></th>
27                 </tr>
28 
29                 @foreach (var item in Model.NewsInfoList)
30                 {
31                 <tr>
32                     <td>
33                         @Html.DisplayFor(modelItem => item.NewsTitle)
34                     </td>
35                     <td>
36                         @Html.DisplayFor(modelItem => item.NewsContent)
37                     </td>
38                     <td>
39                         @Html.DisplayFor(modelItem => item.CreateTime)
40                     </td>
41                     <td>
42                         @Html.ActionLink("Edit", "Edit","News",new { id = item.NewsInfoId },null) |
43                         @Html.ActionLink("Details", "Details", "News", new { id = item.NewsInfoId }, null) |
44                         @Html.ActionLink("Delete", "Delete", "News", new { id = item.NewsInfoId }, null)
45                     </td>
46                 </tr>
47                 }
48             </table>
49         </fieldset>
50     </div>
51     <div style="width:49%;float:right">
52         <fieldset>
53             <legend>Product</legend>
54             <p>
55                 @Html.ActionLink("Create New", "Create", "Product")
56             </p>
57             <table>
58                 <tr>
59                     <th>
60                         @Html.DisplayNameFor(model => model.Product.ProductName)
61                     </th>
62                     <th>
63                         @Html.DisplayNameFor(model => model.Product.ProductDescription)
64                     </th>
65                     <th>
66                         @Html.DisplayNameFor(model => model.Product.CreateTime)
67                     </th>
68                     <th></th>
69                 </tr>
70 
71                 @foreach (var item in Model.ProductInfoList)
72                 {
73                     <tr>
74                         <td>
75                             @Html.DisplayFor(modelItem => item.ProductName)
76                         </td>
77                         <td>
78                             @Html.DisplayFor(modelItem => item.ProductDescription)
79                         </td>
80                         <td>
81                             @Html.DisplayFor(modelItem => item.CreateTime)
82                         </td>
83                         <td>
84                             @Html.ActionLink("Edit", "Edit", "Product", new { id = item.ProductInfoId },null) |
85                             @Html.ActionLink("Details", "Details","Product", new { id = item.ProductInfoId },null) |
86                             @Html.ActionLink("Delete", "Delete", "Product",new { id = item.ProductInfoId },null)
87                         </td>
88                     </tr>
89                 }
90             </table>
91         </fieldset>
92     </div>
93 </div>
複製代碼

這樣,咱們頁面的連接均可以生效。咱們看看以前的頁面連接@Html.ActionLink("Create New", "Create")。咱們簡單的提一下這個方法,這個方法是連接到一個Action。若是不提供Controller名稱,就會在當前contrller中尋找Action,可是這裏咱們的HomeContrller中是沒有Create這個Action的,因此咱們須要提供Controller名稱。@Html.ActionLink("Create New", "Create","News")。這樣咱們全部的頁面均可以生效。可是這裏咱們尚未修改對應的Controller。因此不保證程序能正確的執行。接下來,咱們來修改Contrller,這裏咱們只修改NewsContrller。由於ProductController的修改方法跟NewsContrller是同樣的。
  CreateNew會連接到NewsController的Create Action。咱們在NewsController裏面能夠看到兩個Create方法,可是有區別的是一個Create方法標註了[HttpPost]屬性。這裏HttpPost指的是,當http的請求方式爲post的時候調用這個方法。沒有標註則爲get方式。

複製代碼
 1 public ActionResult Create()
 2         {
 3             return View();
 4         }
 5 
 6         //
 7         // POST: /News/Create
 8 
 9         [HttpPost]
10         [ValidateAntiForgeryToken]
11         public ActionResult Create(NewsInfo newsinfo)
12         {
13             if (ModelState.IsValid)
14             {
15                 db.News.Add(newsinfo);
16                 db.SaveChanges();
17                 return RedirectToAction("Index");
18             }
19 
20             return View(newsinfo);
21         }
複製代碼

能夠看到,沒有標註post的action僅僅只是返回一個view,也就是咱們的編輯界面。而標註了post則是具體的提交數據庫。爲post請求的Create在CreatView中被調用。也就是form的提交。修改和刪除也是跟添加是同樣的道理,這裏咱們只是瞭解便可。之後會詳細講解。代碼這裏我也不列出因此,文章的最後會提供正代碼的下載。

代碼下載

到這裏,全部的代碼就完成了。可是咱們還有兩個很重要的概念沒有解釋

強類型視圖

前面咱們提到,咱們有兩種方式給視圖傳遞數據,一種是使用ViewBag。一種是咱們上面使用的方式。咱們稱之爲強類型視圖。那麼這兩種方式有什麼區別呢?ViewBag從原理上來講實際上是一個數據字典,只不過所存儲的是動態類型。那麼在前段使用的時候咱們是得不到數據的類型的。因此必要的時候咱們須要進行類型轉換。

ViewBag.ViewModel = viewModel;

在使用的時候,咱們須要進行轉換,由於咱們拿不到類型信息

ViewBag.ViewModel.ProductInfoList as IEnumerable<ModelInMVC.Models.ProductInfo>這種方式咱們經過轉換拿到了數據類型。這對於咱們寫代碼來講是比較繁瑣的。咱們也可使用動態類型來迭代。可是又不能使用智能感應。@foreach (dynamic item in ViewBag.ViewModel)。有沒有在一種方式可以不使用轉換,又能智能感應。那就是強類型視圖。

  所謂強類型視圖,不過是指視圖與某類型進行關聯,從根本上講,就是在頁面聲明瞭某一個類型的對象。那麼咱們在頁面的任何地方均可以使用這個對象,這樣就在頁面裏面創建了強類型的對象。也稱之爲強類型視圖。對象的實例化是在返回View的時候進行的return View(viewModel);這樣就完成了對象在頁面的聲明,賦值。以後咱們就可使用了。

模型綁定

在ASP.NET時代,咱們若是要獲取http請求的參數或者窗體提交的值須要使用QueryString或者Request.Form[「」]這樣的方式來獲取值,這種編碼方式是很乏味的,在MVC中模型綁定機制幫咱們解決了這種乏味的編碼方式。咱們以前一直在說約定優先於配置。模型綁定又是一個約定優先於配置的例子。若是有一種方式讓程序幫咱們自動從url或者form中取得數據,而且自動幫咱們構建對象。那麼那個乏味的過程就被取代了。怎麼樣才能讓程序幫咱們構建對象呢?可不能夠,咱們遵循一種約定,那麼程序就會依照這種約定幫咱們構建對象。這種約定就是命名約定,咱們不論是get請求或者post請求。只要按照一種約定去命名咱們的參數,命名咱們的數據。那麼就是可能的。

  • 簡單類型請求:參數的名稱與controller中action參數的名稱一致,那麼模型綁定機制就會幫咱們自動解析數據
  • 複雜類型的請求:在表單中,咱們能夠給輸入元素命名,名稱能夠與屬性名稱相同。模型綁定機制就會根據表單的key和value幫咱們構建對象。

若是不是複雜類型,原理其實也是同樣的。這裏模型綁定組件會在請求中查找數據,這裏的請求能夠是路由數據,查詢字符串和表單集合。

好比public ActionResult Details(int id = 0)這個方法,模型綁定組件,會在請求中尋找name爲id的參數,而後傳遞給方法。若是在操做是有參數的狀況下,模型綁定會隱式的工做。可是咱們也能夠顯式的調用模型綁定

NewsInfo newsinfo = new NewsInfo();
            TryUpdateModel(newsinfo);

這時,模型綁定會顯式的工做,會從請求中查找而且構建對象。模型綁定的副產品就是模型狀態。模型綁定器構建的模型的每個值都會在模型狀態中有一條相應的記錄,來查看模型綁定是否成功。這裏涉及到驗證機制,也就是說,若是完成了全部的驗證,那麼模型驗證就會經過,若是某個值完成不了驗證。那麼模型綁定就會失敗。這也是對提交的值的驗證。ModelState.IsValid驗證模型狀態。若是驗證失敗,那麼模型狀態將包含致使綁定失敗的屬性名,嘗試的值以及錯誤消息。下一篇,我會詳細的講解模型狀態和MVC中的驗證。

相關文章
相關標籤/搜索