ASP.NET MVC4 IN ACTION學習筆記-第四波


ASP.NET MVC4 IN ACTION –View Models

--這是本書的第二部分,前三波是第一部分,這波的知識真的很重要

--在這一波博客里,我會加入一些vs2010的編程技巧,這是在別人那里學不到的

--我會加入一些自己總結的EF方面技巧,這是這本書中暫時還沒看到的

--內容我已經不打算按書中原汁原味的講,因為這波開始,你將要真正的掌握MVC

--每個細節我都會抓的很細,講書中省略的很多東西,但后期我可能不會這樣去做了,因為要提高效率

--所以學習本系列MVC博客的人,可能不能跳着學習了,因為我講過的就不會去再講了

image原著:ASP.NET MVC 4 IN ACTION

本人能力有限,盡量將書中的知識濃縮去講,仔細學過后,然后你再學習其他語言的MVC框架也就大同小異了

本次覆蓋知識點:

  • 1. 用代碼呈現用戶界面(UI/User Interface)(Representing UI concepts in code)
  • 2. 定義一個頁面使用的model(presentation model 或者上波我們提到的view model) (Defining the presentation model)
  • 3. 顯示用戶輸入的數據 (Representing user input)
  • 4. 在復雜的場景中使用 (Scaling to complex scenarios)

 

     茗洋芳竹 飛機票:第一波  第二波  第三波

 

 

    備注:controller action指controller中的action(也就是你們經常寫的方法而已)

            controller actions指controller中全部actions

            action名字加action表示 某某名字的action,例如有個叫Index的action,我就會這樣表示

            Index action.知道了嗎?這方便我寫博客,也更方便理解

            view model指頁面(view)中用到model,我上篇博客說的視圖模型就是view model

            action method指 返回值是ActionResult那個方法,比如Index那個action方法

            user interface,用戶接口(UI的全稱),你可以意會理解成頁面,也就是要跟用戶交互的東西,比如我們給用提供一個頁面,讓用戶錄入數據,這就是一個interface,一個接口,你暫且在這里就是view,就是頁面

            business logic,商業邏輯,類似於數據訪問層中業務實現的邏輯

            domain,你可以理解數據訪問層

            domain model,數據訪問層要使用到的model

            以后我想直接寫英文了,好方便理解,在第二波的博客里我是吃過苦了,文章讀起來好拗口

 

    關於本書的第一部分(前三波內容)講了很多點到即止的知識,大致地概括了一下框架的某些部分。

     現在我們要更深入的研究這些細枝末節

     在本章我們將要討論這個model,怎樣為ASP.NET MVC框架特意地去設計一些更好結構的model。我們還要研究一下Model-View-Controller模式,model通常是很難理解的,因為model在很多地方用到的,說實話,view model,domain model,頁面上的Model對象,@model等等。

     model中定義了很多字段,例如Student這個model定義了Name,id,classname,我們可以想象一下Student是個圓,這個大圓里面有很多小圓

image

假如程序運行了,會給程序一塊內存用來存數據,Student這個圓可以理解為一整塊,一個整體,然后細分Name一塊,Class一塊,Id一塊,我感覺這就是數據結構了,不知道對不對。如果圓更多,是不是可以抽象理解成一個煤球形狀的數據結構,如果數據是這樣分布的,這里隨便說着玩的,我是這樣理解的。

干嘛說這些,因為這些數據都有意義,比如Name代表學生姓名,那么在view中我就可以直接model.Name就可以輸出學生名字了。上一波我們也講到了關於 view model要設計的更有意義和技巧,比如顯示用戶名 和 這個用戶所發的所有comment數,view model中就2個字段,但是如果我們用domain model,那么顯示不太方便。所以上面說要特意地去設計view model。

我感覺這句話挺好的:

When you work with object-oriented languages (such as C#), you create  classes that define this representation. You can create your representation so that when you use it you’re working in a more natural language that allows you to talk about the concepts represented by the software instead of using programming language constructs like Booleans, strings, and integers.

當你在使用面向對象的語言,比如C#,你定義了一個類,這個類定義了頁面上該怎么顯示,你可以創建你的表現形式,不需要再使用domain model(它里面可能含有很多boolean類型,string類型,int類型結構的數據),用更少的代碼就可以表達出你想要顯示的數據的形式,table顯示或者div顯示等等

一句話,domain model能滿足view顯示,你就顯示,你覺得不方便,你可以新建一個model,我們稱為 view model(或者presentation model),這個view model用來在頁面更容易顯示數據

通過本章,有些顯示數據的問題你可以使用view model去簡化在頁面上呈現的邏輯,我們將要看一下view models和input models(把數據從view傳遞給controller,相當於添加數據等等)

 

5.1 什么是view model

          view model的目的很直接--它就是一個model,在一個view里面,為了方便操作數據而特意設計的一個model ,感覺如果還不懂,需要到項目中去意會了。

         在本節中,我們做一個簡單的online store(在線商店)例子來講解view model怎么工作的。了解一下,把view model傳遞給view時候的時候,domain model和view model傳遞的區別,最后我們看一下,input models是如何把view上的數據傳遞給controller的。

 

 

5.1.1 Online Store例子

            准備工作(書中沒有,我大致寫下讓你復習一下)

           image新建項目

            image默認,也添加測試模版,單擊image

           image添加EntityFramework.SqlServerCompact

            image正在安裝…

 

image在Models文件夾中新建一個StoreOnlineContext類

修改這個類,讓它繼承DbContext,按Shift+Alt+F10快速導入命名空間

image

添加一個構造函數,參數,是我們的數據庫名字

image

接下來,我順便教大家快捷方式吧,寫代碼就要快才好,下次我就不寫了

直接在這個類繼續編寫,寫下如下代碼

 image

將光標置入 Customer中去,按下Ctrl+Shift+F10,然后回車鍵,可以快速生成對應名稱的類

同理我們繼續,完成后的樣子

image

然后我們去掉 那個ServiceLevel的DbSet

image

接下來我們向實體類中添加代碼

Customer.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace OnlineStore.Models
   8:  {
   9:      public class Customer
  10:      {
  11:          [Key]
  12:          public int ID { get; set; }
  13:          public int Number { get; set; }
  14:          public string FirstName { get; set; }
  15:          public string LastName { get; set; }
  16:          public bool Active { get; set; }
  17:          public ServiceLevel ServiceLevel { get; set; }
  18:          public ICollection<Order> Orders { get; set; }
  19:      }
  20:  }

 

ServiceLevel.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public enum ServiceLevel
   9:      {
  10:          Standard,
  11:          Premier 
  12:      }
  13:  }

 

Order.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace OnlineStore.Models
   8:  {
   9:      public class Order
  10:      {
  11:          [Key]
  12:          public int ID { get; set; }
  13:          public DateTime Date { get; set; }
  14:          public ICollection<Product> Product { get; set; }
  15:          public decimal TotalAmount { get; set; }
  16:      }
  17:  }

 

Product.cs

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Text;
   6:   
   7:  namespace OnlineStore.Models
   8:  {
   9:      public class Product
  10:      {
  11:          [Key]
  12:          public int ID { get; set; }
  13:          public string Name { get; set; }
  14:          public decimal Cost { get; set; }
  15:      }
  16:  }

 

接下來我們打開HomeController  添加一個新的action,添加好后,我們便來添加好對應的試圖

   1:    StoreOnlineContext _db = new StoreOnlineContext();
   2:   
   3:          public ActionResult Create()
   4:          {
   5:              //添加測試數據
   6:              //1.添加產品
   7:              Product pro1 = new Product
   8:              {
   9:                  ID=1,
  10:                  Name = "大話設計模式",
  11:                  Cost = 39.98M
  12:              };
  13:              Product pro2 = new Product
  14:              {
  15:                  ID = 2,
  16:                  Name = "  WCF服務編程:.NET開發者決戰SOA的制勝利劍(第3版) [平裝]",
  17:                  Cost = 88.5M
  18:              };
  19:              Product pro3 = new Product
  20:              {
  21:                  ID = 3,
  22:                  Name = "  WCF全面解析(套裝上下冊)",
  23:                  Cost = 126M
  24:              };
  25:              ICollection<Product> pros = new List<Product> { pro1,pro2,pro3};
  26:              //2.添加訂單
  27:              Order order = new Order();
  28:              order.ID = 1;
  29:              order.Date = DateTime.Now;
  30:              order.Product = pros;
  31:              order.TotalAmount = 254.48M;
  32:              //3.添加客戶
  33:              Customer customer = new Customer();
  34:              customer.FirstName = "楊";
  35:              customer.LastName = "洋";
  36:              customer.Number = 10000;
  37:              customer.ServiceLevel = ServiceLevel.Standard;
  38:              customer.Active = true;
  39:              ICollection<Order> orders = new List<Order> { order };
  40:              customer.Orders = orders;
  41:              _db.Customers.Add(customer);
  42:              _db.SaveChanges();
  43:              return Content("添加成功!");
  44:          }

對應的Create頁面

image

接下來按下F5運行,默認只有 localhost:端口號,顯示的Home中的Index action的內容

修改瀏覽器地址欄:localhost:你的端口號/Home/Create,過一會,會顯示添加成功

關閉窗口,關掉調試,我們就會發現生成了一個sdf文件,這個是我們第一波博客里面說的SqlServer Compact

image如果沒發現,請點擊顯示全部文件,或者刷新一下解決方案資源管理器

右鍵該數據庫,將它包含到項目中去,我們右鍵打開該文件,就會發現生成的表文件了

imageimage

image

image

image

到這里,我們的數據基本初始化完成了,數據雖然少,但是我們主要是體現的編程思想,下面開始吧!

我們現在有一個這樣的需求:

這個系統的管理員,想要總結Customer的 名字,在線狀態,等級狀態,Order數量,最近的訂單時間

一種方案是:我們用domain model完成,將數據處理后放到頁面展示。我們從數據庫中取出Customers,然后把它傳給view,在view里面,我們循環這個customer集合,然后構造個table展示,在最后一列(最近訂單日期),在view中又要遍歷Customers中的Orders集合,處理一下,然后找出最近的訂單日期。

這種方法會感覺在view上很復雜,秉着盡可能地設計可維護性好的程序的理念,這種方法就要放棄----復雜的循環和計算應該在更高級的一層去處理,view要做的事情就是展示處理后的結果。(這里我又添加了一條數據,但是這次我把Id手動賦值給去掉了,因為數據庫中我給Id那個屬性上面加了Key特性,說明它是個主鍵,自動增長列,ICollection會自動理解為外鍵)

image

我們創建一個view model來解決這個問題。

5.1.2 創建一個View Model

         在Customer Summary頁面上使用一個view model是很正確的。我們這樣設計我們在Models文件中添加一個CustomerSummary.cs文件)

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public class CustomerSummary
   9:      {
  10:          public string Name { get; set; }
  11:          public string Active { get; set; }
  12:          public string ServiceLevel { get; set; }
  13:          public string OrderCount { get; set; }
  14:          public string MostRecentOrderDate { get; set; }
  15:      }
  16:  }

這里面的結構很簡單,都是string類型的。對的,我們所要做的就是在頁面上顯示文本(text)。這樣看起來,簡單直接,很容易見看懂了,不是嗎?presentation  model設計理念,就是在一個view里面,presentation model的設計,要將view要呈現的model的難度降到最低。

這里我解釋一下:presentation  model、view model、domain model的區別:

presentation  model是頁面上最終要使用的一類model的總稱,可以是view model,也可以是domain model(當然,domain model顯示的數據比較簡單,已經不需要在專門去設計一個view model了,它也可以充當presentation  model)。view model是為了方便view顯示數據而有意,有技巧的而設計的model。

打個比方:presentation 對應  一場展覽晚會,model對應服裝,平時domain風格的model也可以去參加這場晚會,有時場合合適,但是有時不適合,在晚會里會遇到很多坎坷,所以我們有必要去換一個風格的服裝,比如view風格的model,它也可以去參加這場晚會,而且精心設計的,我相信view風格的在presentation 表現上更出色。所以我會去選擇view model

不知道你們有沒有聽懂?(哎,這三個英文單詞的區別,我是搞懂了,不知道,你有沒有搞懂?)

5.1.3 把數據傳到 Presentation Model

     在我們的應用程序中,我們等下會建立presentation model,這個model結構和domain model的結構有時幾乎一模一樣的,你可能會懷疑,有必要再建立一個model嗎?用domain model不就夠了嗎,寫兩個model不麻煩嗎?通常如果你用Entity framework框架生成的實體已經夠做domain model了,但有時你真的不能怕麻煩,而不去再建立presentation model,寧願你把ef中生成的實體,你拷貝過去都行,都隨便了,只要有都行,我們真正要在頁面上使用的model都是presentation model,這樣比如 domain model 滿足不了頁面要顯示的額外數據,我們就可以在presentation model中拓展要顯示的屬性就行了,view中使用@model關鍵字 聲明一個強類型的視圖的時候,只能是一個model,假如一個頁面要顯示的數據來自兩個model怎么辦?view中@model只能使用一次,所以你就只能把要顯示的所有內容封裝到一個model中去,而這些數據都來自domain model中的,有的在domain層,可能都是要通過邏輯計算后才能得到的結果,比如 用戶名 發帖數 最近訪問時間,這個發帖數使用通過計算得到的,除非你數據庫設計時,就一張表,字段就這個3個,那無所謂了,隨便了,而view中就只是要顯示這3個字段,所以我們可以建立一個presentation model,里面就這3個字段,全string型的,方便顯示了不是嗎?而view中@model 關鍵字引入的model就是presentation model類型的,就行了.所有復雜的問題都解決了.

每次都要寫presentation model的確很煩,不過不用擔心,這個問題,已經有人考慮了,我們可以使用AutoMapper工具幫我們對presentation model和domain model的相互映射,因為假如 domain model中有很多屬性要賦值到presentation model中去,一行一行寫代碼確實很煩,所以使用AutoMapper就能很快幫我們解決,一行代碼就行了.(我會在第11章講)

(這段內容都是我自己加的,這一波開始,我發現書中把看書者的地位已經放在你已經做過ASP.NET MVC項目了,內容簡寫了好多)

后期你寫項目的時候,就應該能感受到方便了.我們把要顯示的數據使用Presentation model思想封裝的那么好,頁面上很容易就顯示數據了,所以我們就能把開發的重點放在HTML和css樣式,布局前端上面去了,難道不是嗎?

這樣做,反而使的程序更容易測試,維護,開發.寫到這里,我想到一個想法,團隊開發中,我們可以先寫好Presentation model,domain model直接利用工具生成,寫好后,后台程序員直接經過邏輯,將結果封裝成presentation model去就行了.前端只要Model點屬性不就夠了,真的很簡單.

在controller中創建presentation model是不好的,因為controller負責的職責已經很多了,它要決定哪個view被呈現,還有很多其他的工作,所以不要給controller添麻煩了,把這個model提取出去,新建一個類也可以

我們新建一個CustomerSummaryController

image

代碼:

   1:  using OnlineStore.Models;
   2:  using System;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using System.Web;
   6:  using System.Web.Mvc;
   7:   
   8:  namespace OnlineStore.Controllers
   9:  {
  10:      public class CustomerSummaryController : Controller
  11:      {
  12:          //
  13:          // GET: /CustomerSummary/
  14:          StoreOnlineContext _db = new StoreOnlineContext();
  15:          public ActionResult Index()
  16:          {
  17:              IEnumerable<CustomerSummary> model = from o in _db.Customers.ToList<Customer>()
  18:                      select new CustomerSummary
  19:                      {
  20:                          Name = o.FirstName + o.LastName,
  21:                          Active = o.Active? "是" : "否",
  22:                          ServiceLevel =  Enum.GetName(typeof(ServiceLevel), o.ServiceLevel).ToString(),
  23:                          OrderCount = o.Orders == null ? "0" : o.Orders.Count().ToString(),
  24:                          MostRecentOrderDate=(from sub in o.Orders
  25:                                                                orderby sub.Date descending
  26:                                                                select sub).Count()>0?(from sub in o.Orders
  27:                                                                orderby sub.Date descending
  28:                                                                select sub).FirstOrDefault().Date.ToString("yyyy年MM月dd日"):"還沒有訂單"
  29:                      };
  30:              return View(model);
  31:          }

在這里,你發現了,我_db.Customers.ToList<Customer>()

本來是linq  to entity操作,被我轉成了linq to object操作,因為我在linq to entity遇到一個問題,就是ToString的問題,linq to entity不支持ToString,不信你可以試試

在這個方法中,我們返回的是一個 View(對象),這個對象還是一個集合

 

5.1.4 ViewData.Model

Controller和View都共享一個名叫ViewData.ViewData(鍵值對形式的一個很正常的集合),類型叫ViewDataDictionary的一個object

在上個代碼中,我們最后使用了return View(model),這個ViewData.Model就會自動的被賦值了,這里被賦值了一個IEnumerable<CustomerSummary>類型的集合,這個對象在view被呈現之前,就已經全部准備好了,所以你在view中就可以使用這個對象,並使用它里面的值了.這個Model屬性是強類型的,所以我們的view知道怎樣去預測,開發者就可以在vs中就會有智能提示,得到這個好處.這些內部的工作,大部分的Razor視圖引擎都會幫我們做好了,我們使用@model關鍵字

@model IEnumerable<OnlineStore.Models.CustomerSummary>

image

接着上面的那個action,我們添加對應的視圖,代碼如下:

   1:  @{
   2:      ViewBag.Title = "Index";
   3:  }
   4:  @model IEnumerable<OnlineStore.Models.CustomerSummary>
   5:  <style>
   6:       td,th{ height:30px; text-align:center;width:20%}
   7:       table td,th{ border:  #0094ff  solid  1px ;}
   8:      table {width:100%;}
   9:  </style>
  10:  <h2>Custommer Summary</h2>
  11:  <table>
  12:      <tr>
  13:          <th>Name</th>
  14:          <th>Active?</th>
  15:          <th>Service Level</th>
  16:          <th>Order Count</th>
  17:          <th>Most Recent Order Date</th>
  18:      </tr>
  19:      @foreach (var summary in Model)
  20:              {
  21:          <tr>
  22:              <td>@summary.Name</td>
  23:              <td>@summary.Active</td>
  24:   
  25:              <td>@summary.ServiceLevel</td>
  26:   
  27:              <td>@summary.OrderCount</td>
  28:              <td>@summary.MostRecentOrderDate</td>
  29:          </tr> 
  30:              }
  31:  </table>

估計你也看懂了,怎么去使用了.

5.2 表現層的用戶輸入(user input)

就像我們精心設計一個presentation model來呈現數據一樣,我們也要精心設計一個model來獲得用戶輸入的數據.在view里,我們使用很厲害的presentation model使工作變得很簡單,當然一個很厲害的input model也可以在解決獲得用戶輸入的數據后添加到數據庫里這個問題上變得很簡單.以前在ASP.NET里面我們都是通過request請求上key獲得對應的view來獲得數據然后來處理的。這里我們使用input model

5.2.1 設計model

我們新建一個類

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public class NewCustomerInput 
   9:            { 
  10:                 public string FirstName { get; set; }                  
  11:                 public string LastName { get; set; }                                                
  12:                 public bool Active { get; set; } 
  13:            }
  14:  }

這個model主要接受用戶輸入的數據,跟view都是一一對應的

image

5.2.2 設計model

接下來我們在CustomerSummaryController中添加一個action

   1:        public ViewResult Save(NewCustomerInput input)
   2:          {
   3:              return View(input);
   4:          } 

添加完NewCustomerInput,F5運行下程序,然后我們再右鍵Save名稱,添加對應的view

image選中后,我們點確定image

當然我們可以不選擇模型類,自己添加對應的view后,自己手動加代碼,這里只是列出一個技巧

在這個Save action中,我們傳了一個NewCustomerInput類型對象的參數,這個值就會被ASP.NET MVC中的DefaultModelBinder相互綁定,view中就可以把值封裝好后傳過來,在ASP.NET MVC默認是這樣定義的.

我們添加好view后,我們就成功把一個view變成一個強類型的view,ViewPage<T>,其中T已經被指定為NewCustomerInput對象了,也就意味着,ViewData.Model對象已經是NewCustomerInput類型的了。然后我們在精心設計一個form(表單)就可以完成了.

   1:  @model OnlineStore.Models.NewCustomerInput
   2:   
   3:  @{
   4:      ViewBag.Title = "Save";
   5:  }
   6:  <div>
   7:      <form action="@Url.Action("Save")" method="post">
   8:          <fieldset>
   9:              <div>
  10:                  @Html.LabelFor(x => x.FirstName)
  11:                  @Html.TextBoxFor(x => x.FirstName)
  12:              </div>
  13:              <div>
  14:                  @Html.LabelFor(x => x.LastName)
  15:                  @Html.TextBoxFor(x => x.LastName)
  16:              </div>
  17:              <div>
  18:                  @Html.LabelFor(x => x.Active)
  19:                  @Html.CheckBoxFor(x => x.Active)
  20:              </div>
  21:              <div>
  22:                  <button name="save">
  23:                      保存</button>
  24:              </div>
  25:          </fieldset>
  26:      </form>
  27:  </div>

image指定了這個表單post到哪個action去處理,這里指定了Save

還有我們使用了lambda表達式,綁定NewCustomerInput中的屬性

例如,@Html.TextBoxFor(x=> x.LastName)等同於代碼

<input type="text" name="LastName" />

你寫這行代碼和C#風格的綁定效果都是一樣的.

  Lambda在重構(refactoring)中的幫助

   不要小看view中的lambda表達式,它們在你寫代碼的時候就在編譯,所以你改了action,這個代碼就會在編譯的時候報錯,所以在你的view中寫代碼時候,對比下你視圖中引用的類,還有返回string的方法--你只有在運行的時候會發現這些錯誤.

   在refactoring中當然還有其他的強類型視圖數據引用助手,我們可以使用JetBrains ReSharper(www.jetbrains.com/resharper),它將幫助你重構代碼,所有的視圖都可以使用它,一個很強大的工具

 

再使用強類型助手之前,我們還是依靠 magic(魔力的)strings,編程者們要手動地去確保它們和input form一致,使用強類型助手,就像上面view中的代碼,ASP.NET MVC已經為我們處理了,所以把input model中的屬性改名了,也不會在屏幕上顯示錯誤,程序順利運行,但是打開錯誤的頁面,還是會顯示綁定錯誤的.

image這里FirstName后我故意添加了一個s

按下F5的時候,程序可以運行,無影響,但是指定到使用頁面的時候,就會顯示這個綁定的錯誤.就是這個效果

5.2.3 處理提交的input model

在上面那個view中,我們post到Save action,ASP.NET MVC提供了一個很簡單的方式,把HTTP請求轉換成一個model,這個過程我們叫model binding,第十章我們會仔細去講,我們看一下這個action

image

接下來我們新添一個ActionResult Save2,上面那個action返回的是ViewResult

image

修改對應的view中post請求的action,為Save2

image

按下F5運行項目,輸入新地址

image

image

image

然后輸入http://localhost:<你的port>/CustomerSummary/Index

image

我們再修改下Save2 action,return 新的action---Index,這樣添加成功后,就會跳轉到Index action,而Index action返回的是一個view,也就是Customer Summary列表

image

小提示:修改view中的代碼的時候,修改好后,保存,不必重新運行項目,直接刷新瀏覽器就可以立即看到刷新后的效果

如果程序的服務器沒有關,但是vs中的代碼已經停止運行了,此時修改view中的代碼,然后保存,也不必重新運行項目,直接刷新瀏覽器就可以立即看到刷新后的效果.就等同於修改一個html一樣,很方便,但是修改后台的代碼,就不行了,就必須要重新運行項目.

許多view不會去顯示或者一個input forms,而是整合很多elements(網頁上一級一級的節點),來達到一個富客戶端的體驗.在一下節里,我么將會應用這些我們在本波里已經學到的concept(概念或者思想),來實現一個更復雜的view

 

5.3 更復雜的model,display和input並存

在這一節里面,我們將構造一個view model,既顯示數據,也把input model中最后修改的active狀態數據發送到服務器上,做修改工作

image

5.3.1 設計展示和Input都存在的model

我們新建一個CustomerSummary2類

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Web;
   5:   
   6:  namespace OnlineStore.Models
   7:  {
   8:      public class CustomerSummary2
   9:      {
  10:              public string Name { get; set; }
  11:              public string ServiceLevel { get; set; }
  12:              public string OrderCount { get; set; }
  13:              public string MostRecentOrderDate { get; set; }
  14:            public CustomerSummaryInput inputs { get; set; }
  15:          public class CustomerSummaryInput {
  16:              public int Number { get; set; }
  17:              public bool Active { get; set; }
  18:          }
  19:      }
  20:  }

 

這里我們在一個類中嵌套另一個類,之后,在user interface上,在顯示的時候,input elements就會被嵌套,把CustomerSummaryInput這個Input model寫在這里面,方便以后的維護,因為他和這一個view在一起顯示,在其他view中也沒有用到啊

注意在CustomerSummaryInput中的Number屬性--它是每一個customer中的編號(Id),現在我們凍結叫楊洋的這個customer

5.3.2 讓Input Model開始工作

我們再新建一個action,MoreSave action,Save3 action

   1:    public ViewResult MoreSave()
   2:          {
   3:              IEnumerable<CustomerSummary2> model = from o in _db.Customers.ToList<Customer>()
   4:                                                   select new CustomerSummary2
   5:                                                   {
   6:                                                       Name = o.FirstName + o.LastName,
   7:                                                       ServiceLevel = Enum.GetName(typeof(ServiceLevel), o.ServiceLevel).ToString(),
   8:                                                       OrderCount = o.Orders == null ? "0" : o.Orders.Count().ToString(),
   9:                                                       MostRecentOrderDate = (from sub in o.Orders
  10:                                                                              orderby sub.Date descending
  11:                                                                              select sub).Count() > 0 ? (from sub in o.Orders
  12:                                                                                                         orderby sub.Date descending
  13:                                                                                                         select sub).FirstOrDefault().Date.ToString("yyyy年MM月dd日") : "還沒有訂單",
  14:                                                       inputs=new CustomerSummary2.CustomerSummaryInput{
  15:                                                        Active=o.Active,
  16:                                                        Number=o.Number
  17:                                                       }
  18:                                                   };
  19:              return View(model);
  20:          }
  21:   
  22:          [HttpPost]
  23:          public ActionResult Save3(List<CustomerSummary2.CustomerSummaryInput> input)
  24:          {
  25:              foreach (var item in input)
  26:              {
  27:                  Customer cus = _db.Customers.Where(x => x.Number == item.Number).FirstOrDefault();
  28:                  cus.Active = item.Active;
  29:                  _db.SaveChanges();
  30:              }
  31:              return RedirectToAction("MoreSave");
  32:          }

 

我們添加對應的視圖:

   1:  @model IEnumerable<OnlineStore.Models.CustomerSummary2>
   2:  @{
   3:      ViewBag.Title = "MoreSave";
   4:  }
   5:  <style>
   6:      td, th {
   7:          height: 30px;
   8:          text-align: center;
   9:          width: 20%;
  10:      }
  11:   
  12:      table td, th {
  13:          border: #0094ff solid 1px;
  14:      }
  15:   
  16:      table {
  17:          width: 100%;
  18:      }
  19:  </style>
  20:  <h2>Custommer Summary</h2>
  21:  <form action="@Url.Action("Save3")" method="post">
  22:      <table>
  23:          <tr>
  24:              <th>Name</th>
  25:              <th>Service Level</th>
  26:              <th>Order Count</th>
  27:              <th>Most Recent Order Date</th>
  28:              <th>Active?</th>
  29:          </tr>
  30:          @foreach (var summary in Model)
  31:          {
  32:              <tr>
  33:                  <td>@summary.Name</td>
  34:                  <td>@summary.ServiceLevel</td>
  35:   
  36:                  <td>@summary.OrderCount</td>
  37:                  <td>@summary.MostRecentOrderDate</td>
  38:                  <td>
  39:                      <input type="checkbox" name="Active" checked="@summary.inputs.Active"/>
  40:                      <input type="hidden" name="Number" value="@summary.inputs.Number" />
  41:                  </td>
  42:              </tr> 
  43:          }
  44:      </table>
  45:      <div>
  46:          <button name="save">
  47:              修改Active</button>
  48:      </div>
  49:  </form>

按下F5運行,就可以看到一個列表的效果,Active列已經自動幫我們勾選上了

接下來修改active,關於這個,這里的Save3沒有成功實現,還有問題,如果讓我實現這個功能,我不會采用嵌入類的形式的,我自己試着用嵌入類的方式去寫了,但是沒有成功,你們能幫我解決嗎?

在這一波教程里面,書里面省略了很多的代碼,而且都簡化了,博客寫到這里,還有點可惜,因為把修改后的狀態保存回去,還沒有完成.

這一波的代碼我就不上傳了,手寫練習吧,如果你有利用嵌入類完成了最后的這個效果,你能告訴我思路嗎?

 

在下一波的教程里,我們主要講Validation ,驗證,再下下一波里,我們主要講AJAX方面的,這兩波內容都很多,也都是很重要的部分

關於ASP.NET MVC4 IN ACTION系列目錄地址已經生成:點擊查看目錄

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM