2010年10月24日 星期日

ASP.NET Dynamic Data - How to customize error message with template

本篇在說明如何使用Dynamic Data 專案預設範本後,如何客製化Error Message。

首先我們建立一個Dynamic Data專案,如下圖:

我們打開專案瀏覽,其目錄層次如下圖,然後我們以Northwind資料庫為例,建立一個名為Northwind的實體資料模型。

這樣就完成了,目前還沒修改到任何程式,然後我們Compile本專案,下圖為Compile後的第一頁,其中如Products,Customers等等就是我們在Northwind的實體資料模型中,所選擇要連結的資料表。

我們選擇Edit這筆資料。

Default Error Message


我們在UnitPrice這個欄位輸入A,因為這個欄位型態應該為數值,故目前輸入的A是字串,所以系統會顯示預設的欄位驗證後的Error Message「欄位UnitPrice必須是有效的十進位數」,如下圖:

如何Customize Error Message


接下來我們要客製化上面所提到的Error Message(Cuctomize Error Message),我們先建立一個Partial Products Class,然後在這裡面寫入我們針對每個欄位要驗證的項目,以及驗證錯誤後,我們客製的錯誤訊息,如下範例程式碼所示:


   1:  using System;
   2:  using System.ComponentModel.DataAnnotations;
   3:  using System.Web.DynamicData;
   4:   
   5:  namespace WebAppDynamicData.Models
   6:  {
   7:      [MetadataType(typeof(ProductsMetadata))]
   8:      public partial class Products
   9:      {
  10:          public class ProductsMetadata
  11:          {
  12:              [StringLength(8, ErrorMessage = "ProductName超過欄位長度")]
  13:              public object ProductName;
  14:   
  15:              [Required(ErrorMessage = "UnitPrice欄位不可為空白")]
  16:              [Range(100, 2000, 
  17:                  ErrorMessage = "100<=UnitPrice允許值<=2000")]
  18:              public object UnitPrice;
  19:   
  20:              [RegularExpression(@"^\d+$", 
  21:                  ErrorMessage = "UnitsInStock欄位只可為數字")]
  22:              [DataType(DataType.Currency, ErrorMessage = "TypeError")]
  23:              public object UnitsInStock;
  24:          }
  25:      }
  26:  }

Display Customize Error Message


寫完上面的驗證項目與客製化的錯誤訊息後,我們來測試一下是否可出現我們剛才設定的客製化的錯誤訊息。我們將UnitPrice這個欄位先清為空白後,按下下方的Update,進行修改後儲存的動作,我們可看到我們剛才客製化的錯誤訊息「UnitPrice欄位不可為空白」。

我們再接著策是UnitPrice欄位值是否符合在一定的數值範圍內,我們在UnitPrice欄位輸入2001後按下Update,進行修改後儲存的動作,我們可看到我們剛才客製化的錯誤訊息「100<=UnitPrice允許值<=2000」。



以上我們示範了如何使用Dynamic Data 專案預設範本後,還能客製化我們想要顯示的錯誤訊息。

2010年10月21日 星期四

設定Entity Data Source 時發生的error - Unable to load the specified metadata resource

有時我們在建立好Entity Data Source控制項與Entity Framework模型設定後,如下圖:


準備要去設定Entity Data Source控制項的各項屬性資訊時,會遇到一個錯誤訊息

Unable to load the specified metadata resource

如下圖所示:


這使得我們無法設定Entity Data Source的資料對應來源,也就無法使用Entity Data Source這個控制項來當作是網頁的資料來源。


Re-Build Project


解決的方法,就是再Re-Build一次整個專案,如下圖:


Re-Build完整個專案後,我們再次去設定Entity Data Source控制項時,就不會有錯誤訊息出現:



最後完成選擇要對應的資料欄位後,按下Finish按鈕,就完成了設定Entity Data Source。

2010年10月20日 星期三

ASP.NET 4 Dynamic Data / Scaffolding Web Application Sample

最近梅姬(Megi Typhoon 2010/10/16)颱風號稱是破表的18級風,而且有人說根本就是批著颱風外衣的超級龍捲風,NASA下面這張圖清楚看到粗壯的颱風眼。這跟我們這個主題也有點小關係,我們也期望ASP.NET Dynamic Data 也是一個威力強大新的且快速的動態呈現資料的方式。

From:http://www.nasa.gov

ASP.NET Dynamic Data可自動且輕鬆的建立由資料驅動的Web應用程式,配合啟用Scaffolding機制,可不必一直再重覆開發維護資料的網頁,有關Dynamic Data架構簡單的說明可區分為三層:

1.展示層:如使用Edit/Add等等Template來呈現資料給使用者。
2.資料層:如建立自訂的資料結構,或是設定ASP.NET的資料來源控制項。
3.資料對應層:如使用Entity Framework / LINQ to SQL 對應到資料庫的各種資料Table。


以下以實際Northwind資料庫為範例,使用Dynamic Data專案範本中預設的網頁Template,透過三個步驟,可建立簡單而完整的Dynamic Data Web Application。

1.建立Dynamic Data專案。
2.建立資料對應層的實體模型設定(使用Entity Framework)。
3.在Global.aspx中註冊Entity Framework資料內容。


建立Dynamic Data專案


首先我們使用VS Web Developer 2010 Express,選擇New Project直接建立Dynamic Data Entities Web Application,我們有兩個專案範本可選擇來直接建立Dynamic Data 的應用程式:


1.ASP.NET Dynamic Data Entities Web Application
2.ASP.NET Dynamic Data Linq to SQL Application


因為我們這裡的資料對應層使用ASP.NET Entity Framework,所以這裡選擇第1種專案範本,建立一個ASP.NET Dynamic Data Entities Web Application當做範例。




基本上兩種專案範本使用與建立沒有太多的差別。小小差異處為建立後,可以查看專案中的reference元件,兩種不同的專案,系統預設包含的元件會不一樣,ASP.NET Dynamic Data Linq to SQL Application的專案會多了Linq to SQL會使用的元件,如System.Data.Entity、System.Data.Linq、System.Drawing與System.Runtime.Serialization等。當然,如果開發者要在ASP.NET Dynamic Data Entities Web Application專案建立後,再去拉Linq to SQL的設定來使用,也是可以,但要記得確認專案檔中是否已經設定参考到這些元件。不過這樣就乾脆使用第2種專案範本來建立專案檔就好了。


建立資料對應層的實體模型設定(使用Entity Framework)


接下來我們在專案檔中建立Models目錄,在該目錄下新建一個名為Northwind.edmx的Entity Framework設定檔案,名稱為NorthwindEntities,當然也可建立在別的目錄,我這邊是比照MVC的方式來處理,這樣程式擺放位置比較有經過歸類,不會散得到處都是。(如何建立Entity Framework可参考本文最後相關文章連結)




在Global.aspx中註冊Entity Framework資料內容


打開Global.aspx檔案,尋找一下RegisterRoutes()這個method,並且加上下列程式:


   1:  using WebAppDynamicData.Models;
   2:   
   3:  public static void RegisterRoutes(RouteCollection routes)
   4:  {
   5:      DefaultModel.RegisterContext(typeof(NorthwindEntities),
   6:          new ContextConfiguration() { ScaffoldAllTables = true });
   7:                  :
   8:                  :
   9:  }


其中NorthwindEntities就是我們在上面設定的Entity Framework資料模型名稱,這樣可讓系統知道要註冊NorthwindEntities這個實體模型設定供動態呈現資料時使用,也是啟動Scaffolding機制(何謂Scaffolding)。


Woo.....資料與CRUD功能可以動態與自動地呈現了


編譯上述專案檔後,我們好像還沒寫到程式,APS.NET就已經將平常維護與查詢資料各種機制都自動建立完成了,如下這是default.aspx的畫面,其中的customers,products...等等,就是我們之前在NorthwindEntities這個實體模型自資料庫中所設定要呈現的Table資料。



接著我們點進products來看看,如下圖可看到CRUD(增加/查詢/修改/刪除)機制都已經預設建立好了。



基本的查詢下拉選項也可使用,如下圖:




更酷的是,系統也會自動顯示 資料庫中的foreign key對應可讓我們在product列表資料中,直接就可知道哪些訂單中有訂購這項產品,這項產品供應商是哪家公司,如下兩圖:


點選Order_details欄位資料後,可看到哪些訂單中有訂購這項產品。




點選Suppliers欄位資料後,可看到供應商是哪家公司,及這家公司的基本資料。




CRUD AUTOMATICA


我們也可以使用這些自動建立好的CRUD機制,下面是我們點選每筆資料最前面的Edit Link後,可讓我們直接修改每筆資料的欄位,並且可自動驗證使用者輸入的資料型態是否正確,如下兩圖:




我們點選表格下方的insert new item Link後,可讓我們增加一筆資料,並且可自動驗證使用者輸入的資料型態是否正確,如下兩圖:




我們點選每筆資料最前面的Delete Link後,會先再次詢問是否刪除此筆資料,按下確定後,則可刪除這筆資料,如下兩圖:



我們點選每筆資料最前面的Detail Link後,我們可單獨查看這筆資料明細,如下兩圖:



Dynamic Data可達到Scaffolding機制,就是設定好資料對應結構後(Entity Framework/LINQ to SQL),就可動態產生資料頁面供查詢與維護,不必再實際為每一個種維護需求建立實體的網頁程式。


相關聯結:

ASP.NET Dynamic Data 概觀
建立使用 Scaffolding 的新 Dynamic Data 網站
ASP.NET 動態資料 Scaffolding 和頁面範本概觀

如何建立Entity Framework可参考下列:

Entity Framework Concept
MVC Entity Framework
微軟Entity Framework教學影片

2010年10月10日 星期日

.NET 4 + MVC2 設定真正 ValidateRequest = false 並回應更友善的錯誤頁面與錯誤訊息

因為.NET 4中,對惡意程式碼的欄截(AntiXSS),已經提升到BeginRquest的層面了,請看ASP.NET4 WhitePaper,若我們再搭配MVC2架構,想要「不啟用」ValidateRequest,則原先我們要在View頁面上自訂

<%@ Page Language="C#" ValidateRequest="false" %>

或者是在Action上方加上[ValidateInput(false)]

   1:  [ValidateInput(false)]
   2:  public class HomeController : Controller
   3:  {
   4:      :
   5:  }

上面這兩個方式都還是會拋出錯誤畫面如下圖,無法真正的如同之前可以將ValidateRequest設為「不啟用」的狀態,所以我們在Action內並無法欄截使用者輸入的惡意程式(如<script>....</script>)。


如何提供給使用者更友善的介面與錯誤訊息

假設我們現在有一個登入頁面如下圖,讓使用者輸入UserID與UserPassword,我們希望若是使用者輸入惡意程式碼,我們要在應用程式這個層面加以欄截並且顯示出錯誤訊息給使用者看,當然我們也可以直接導到一個客製化的error page,但是這不是很友善的介面,因為使用者還要再自行連回登入頁面,然後再輸入一次UserID與UserPassword。



我們可以透過下面的步驟,實現我們在.NET 4 + MVC2 中將惡意輸入程式碼加以欄截並且顯示出錯誤訊息給使用者。

1. web.config中

我們先設定web.config中加上如下的requestValidationMode

   1:  <system.web>
   2:      <httpRuntime requestValidationMode="2.0" />
   3:  </system.web>

2.controller中

因為之前提過.NET 4中對惡意程式碼的欄截(AntiXSS),已經提升到BeginRquest的層面了,所以我們必須在每一個controller在啟始化過程中,設定ValidateRequest=false,然後我們就可在這個controller的Action中去欄截與判斷使用者輸入的每一個正常與惡意的字元。下面為我們再Request的Initialize時候,加上ValidateRequest=false。

   1:  [HandleError(View = "Error")]
   2:  public class HomeController : Controller
   3:  {       
   4:      //一定要加上去 
   5:      protected override void Initialize(RequestContext requestContext)
   6:      {
   7:          this.ValidateRequest = false;           
   8:          base.Initialize(requestContext);
   9:      }
  10:   
  11:      [HttpPost]
  12:      public ActionResult Index(User user)
  13:      {
  14:          if (!ModelState.IsValid)
  15:          {
  16:              return View(user)
  17:          }    
  18:              :
  19:              :
  20:      }
  21:  }

3.使用者類別(User.cs)中

可以按照MVC2規範中的方法,我們去設定每個欄位所要檢查的各種條件,與回應的錯誤訊息。

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.ComponentModel;
   5:  using System.ComponentModel.DataAnnotations;
   6:  using HoraceRobot.MVC2.Models;
   7:   
   8:  namespace HoraceRobot.MVC2.ViewModels
   9:  {
  10:      public class User
  11:      {
  12:          [Required(ErrorMessage = "不可為空白")]
  13:          [RegularExpression("^[0-9a-zA-Z_]{1,12}$", 
  14:              ErrorMessage= "使用者帳號錯誤")]
  15:          public string userID { get; set; }
  16:   
  17:          [Required(ErrorMessage = "不可為空白")]
  18:          [RegularExpression(@"^[^\<^\>]{6,16}$",
  19:              ErrorMessage= "使用者帳號錯誤")]
  20:          public string userPwd { get; set; }    
  21:          :
  22:          :
  23:      }
  24:  }

透過上面的設定與程式的撰寫,我們實際來驗證一下,是否可達到我們想要的,就是可以將惡意程式碼欄截並提供使用者更友善的介面與錯誤訊息。現在假設我們輸入一些不正確且具惡意的程式碼當作是登入頁的UserID與UserPassword,如下圖:


按下登入按鈕後,可以發現,此時我們可以真的欄截這些惡意的程式碼,並且透過上述第3點User類別中,我們所設定的欄位檢核條件與錯誤訊息,回應給登入頁錯誤訊息。而這些並非是我們自己撰寫很多客製化的程式,而只是遵循著ASP.NET MVC2中的各種規範去進行使用者輸入欄位驗證與檢核,配合在Controller中BeginRequest的時候去設定ValidateRequest = false來達成我們的需求。如下圖:


另外,因為我們將ValidateRequest = false,且在web.config中將requestValidationMode設回之前的2.0版本,這的確是有失.NET 4增強對抗惡意程式或XSS攻擊的美意,但是安全性的措施實在不嫌多,雖然我們按照MVC2的規範,我們都已經會對輸入與輸出前加上Html.Encode()或是Url.Encode(),但若想要再加強網站的安全防護,也可以参考Phil Haack的文章--Using AntiXss As The Default Encoder For ASP.NET,進一步增強網站Encoder機制,去防止全面性的XSS攻擊。

2010年10月5日 星期二

LINQ 使用Between查詢兩個Datetime範圍之間的資料

使用LINQ或者是Entiry Framework的時候,我們希望可以下兩個起迄日期欄位,然後取得這兩個日期欄位之間的資料,我們也都知道LINQ並沒有between這個我們熟悉的SQL語法可用,然後再加上日期格式,嘿..好像還好,但是也夠我們想好一會兒了。

下面我們舉出實際的例子,透過Entity Framework去查詢出2010/05/07到2010/05/10這兩日期間的資料。當然下面程式中ClickDate這個欄位在表格TrackStatistic中是日期型態欄位,當然我們須進行下面兩個檢查,但這裡主要是說明LINQ中表達方式,檢查資料方式就暫不討論了。
1.先判斷這兩個查詢的日期欄位是符合日期格式(must datetime format)。
2.起日(start date) <= 迄日(end date)。






檢查完資料,在將参數帶入LINQ查詢語法之前,我們須將迄日(end date)加上1天,這樣查詢可精準的抓到小於及等於迄日的資料。如下舉在MVC2中Show()這個Action希望取出兩個日期間的資料:



   1:  public ActionResult Show()
   2:  {
   3:      DateTime sd = Convert.ToDateTime("2010/05/07");
   4:      DateTime ed = Convert.ToDateTime("2010/05/10").AddDays(1);
   5:      TESTDBEntities _db = new TESTDBEntities();
   6:      var showToView = _db.TrackStatistic.Where(
   7:          m => m.ClickDate >= sd && m.ClickDate <= ed);
   8:      return View(showToView);
   9:  }


上面這段LINQ查詢後可精準的抓出兩個日期間的資料,而且entity framework會自動只比對日期欄位的年月日,查詢後資料如下圖所示:

2010年10月4日 星期一

使用DateTime.TryParse()驗證欄位值是否為日期格式

當我們在作資料欄位型態檢查時,遇上要檢查日期欄位的資料型態,總是會一時愣在那邊,心想是否要寫一大段判斷年月日等等是否符合規則的程式語法,期時不需要,我們可以利用TryParse()的方式,快速與簡單的判斷某個使用者輸入是否符合日期格式。假設我們要驗證名稱為txtDateStart的字串是否輸入值服何日期格式,如下範例程式:


   1:  public bool CheckDateTimeType(string txtDateStart) 
   2:  {
   3:      DateTime sd;//供判斷暫存之用
   4:      if (String.IsNullOrEmpty(txtDateStart) || 
   5:          !DateTime.TryParse(txtDateStart, out sd)))
   6:      {
   7:          return false;
   8:      }
   9:      return true;
  10:  }

當然,要注意這個TryParse是與Web Server當時的所設定的文化語系(culture)相關,如「2010/01/31」這個字串用在台灣作業系統(Operation System)環境與設的的zh-TW語系,則TryParse()會回應true,判斷是日期格式;但是「2010/01/31」用在en-US等語系中,TryParse()有可能會回應false。

2010年10月3日 星期日

Other Way For Setting Customer Error Page in MVC2

本文舉出3種方法去控制MVC2中,如何自訂Error Page,釐清在各個View中加入專屬於某個View的自訂Error page,或者是整個系統共用一個自訂Error Page的實際作法,一般微軟官方文件或其它技術Blog中並沒有針對這點加以整理與說明。


方法一:整個系統共用一個自訂Error Page


步驟如下:
1.建立共同的error page,在/Views/Shared/Error.aspx。
2.在相對的controller的class上加上[HandleError],然後並不用指明要將錯誤頁導到哪一個error.aspx中,系統會自動導到/Views/Shared/Error.aspx。

   1:  [HandleError]
   2:  public class ProductController : Controller
   3:  { }

3.Web.config上

   1:  <customErrors mode="RemoteOnly" defaultRedirect="/Views/Shared/Error.aspx">
   2:  </customErrors>

測試的時候,<customErrors mode="RemoteOnly" ...>請改為<customErrors mode="On" ...>。

4.則此時有錯誤產生系統先將錯誤頁導到/Views/Shared/Error.aspx。


方法二:每個Views目錄內設定專屬的自訂Error page


步驟如下:
1.每個Views目錄內各自加上Error.aspx,如Views/Product/Error.aspx或Views/CustomerError.aspx。
2.在相對的controller的class上加上[HandleError(Vies="Error")]。


   1:  [HandleError(Vies="Error")]
   2:  public class ProductController : Controller
   3:  { }

3.Web.config上

   1:  <customErrors mode="RemoteOnly" defaultRedirect="/Views/Shared/Error.aspx">
   2:  </customErrors>

測試的時候,<customErrors mode="RemoteOnly" ...>請改為<customErrors mode="On" ...>。

4.則此時有錯誤產生系統先將錯誤頁導到Views/Product/Error.aspx。


方法三:在Action內指明專屬的自訂Error page


步驟如下:
1.每個Views目錄內各自加上Error.aspx,如Views/Product/Error.aspx或Views/CustomerError.aspx
2.在相對的controller內每個method中加上try .... catch,如下範例:

   1:  public ActionResult Create(FormCollection collection)
   2:  {
   3:      try
   4:      {            
   5:          //處理Create Product
   6:      }
   7:      catch (Exception ex)
   8:      {
   9:          ViewData["msg"] = ex.Message + " " + ex.InnerException;
  10:          return View("Error");
  11:      }
  12:  }

3.則此時有錯誤產生系統先將錯誤頁導到Views/Product/Error.aspx。

希望上述實作經驗中的範例可以對大家在MVC的自訂Error page能有所幫助。

MVC2 Trim String

在MVC2中,加設我們有一個產品(Product)的資料需要維護,我們在Edit或者是Create的Form中,我們修改了一些欄位上的資料,我們希望可以將每個欄位多餘的空白字元去除(trim space),以免儲存回資料庫後,再次取出時,會產生錯誤,例如我們如果在Create的Form中,將產品的識別碼(PrdID)給輸入「A001  」,後面部小心多了一些空白字元,若是沒有在儲存回資料庫前,將這個欄位後面的空白去除掉,當這項產品儲存在資料庫後,我們再次輸入「A001」,要將這項產品編號的產品給查詢出來時,系統就會告訴我們找不到這項產品。因為資料庫存的是「A001  」,而非「A001」。本文基於MVC的架構,自View-Colltroller-Model這三個層次順序,由外圍到底層,說明如何簡單的使用既有的方法去達成去除字串後面多餘的空白字元。


1.在View中使用javascript/jQuery


當然在View的層面,可以用javascript或者是jQuery在使用者前端的瀏覽器上就針對這個PrdID欄位輸入的空白字串給去除,如用下列的方法可以達到去除字串前後空白(Right trim and Left trim)

   1:  function trim(stringToTrim) {
   2:      return stringToTrim.replace(/^\s+|\s+$/g, ""); 
   3:  }

2.在Colltroller中去除字串欄位後面的空白(Trim string in Colltroller)


本文中,我們主要針對在MVC2架構中如何進行去除輸入欄位字串後面空白的方法,這裡下面例子可以在接收Post回Colltroller的Create Action中以collection["PrdID"].Trim()這個方法,去除使用者不小心輸入PrdID這個欄位後面的空白字元,然後再將正確的PrdID這個字串儲存到資料庫中,如下:

在MVC2專案中程式位置://colltroller/ProductColltroller.cs

   1:  :
   2:  [HttpPost]
   3:  public ActionResult Create(FormCollection collection)
   4:  {
   5:      Product appToCreate = new Product();
   6:      prdToCreate.ModelState = this.ModelState;
   7:      collection.Set("PrdID", collection["PrdID"].Trim());
   8:      TryUpdateModel(prdToCreate, collection);
   9:      if (ModelState.IsValid)
  10:      {
  11:          //儲存至資料庫
  12:      return View("Success");
  13:      }
  14:      else 
  15:          return View(prdToCreate);    
  16:      }
  17:  }
3.在Entity Framework Model中去除字串欄位後面的空白(Trim string/value in Model)


這個方法,我們需打開MVC2專案中Model目錄內Entity Framework設定的Product.Designer.cs這個程式,自Model這個層面去移除使用者不小心輸入PrdID這個欄位後面的空白字元.這裡下面例子可以看到我們再set這個值之前,就用string.IsNullOrEmpty()這個方法,去除PrdID這個欄位後面的空白字元(如果有的話),然後再將正確的PrdID這個字串儲存到資料庫中,如下:
在MVC2專案中程式位置://Models/Product.Designer.cs

   1:  :
   2:  #region Entities
   3:  :
   4:  [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]
   5:  [DataMemberAttribute()]
   6:  public global::System.String PrdID
   7:  {
   8:      get
   9:      {
  10:          return _PrdID;
  11:      }
  12:      set
  13:      {
  14:      value = (string.IsNullOrEmpty(value)) ? value : value.Trim();
  15:          OnPrdIDChanging(value);
  16:          ReportPropertyChanging("PrdID");
  17:          _PrdID = StructuralObject.SetValidValue(value, true);
  18:          ReportPropertyChanged("PrdID");
  19:          OnPrdIDChanged();
  20:      }
  21:  }

Remove Prevent Saving Changes in MS SQL Server 2008 R2

當我們安裝好MS SQL Server 2008 R2時,建立了一個表(Table)如下圖。如同所有到達完美之路的過程,總是需要調整自己,此時我們還需要修改這個Table的某一個欄位,如下圖我們將CategoryName這個欄位改成准許Null的欄位,改好後,如下圖


總如往昔我們總經驗過的事情,到達完美的路上,總也是會遇到挫折,我們按下儲存時,
哇~出現了下列的錯誤訊息,告知我們目前一旦建立好Table後,預設是不能再修改這個Table的Schema。我們只是忠實的告訴資料庫,嘿~你錯了!但是像科幻電影般,系統也有了基本的智慧去告訴你,嗨~你也錯了,你不能做這件事。


如果遇到這種狀況,我們可以這樣做,打開選單列上的工具(Tools)->然後按下
選項(Options)


接著我們可以經過下列步驟來解除這項安全性的設定:
1.選擇設計者(Designers)->
2.選表格與資料庫設計者(Table and Database Designers)->
3.看右邊的表格選項列表(Table Options)->
4.將預防儲存重建資料Schema選項勾勾拿掉(Unchecked Prevent saving changes that
require table re-creation)


完成上述設定,我們再重複一開始我們修改Table的Schema,然後按下儲存,這樣就
可以順利完成這項Table Schema的異動。