MVCラーニングシリーズ-フォームデータ提出


Asp.Net MVCプロジェクトでは、Viewがページの展示・収集を担当し、展示されたデータはControllerのActionから取得し、収集したデータをControllerのActionに提出する.ここでのデータは、ベースタイプ、モデル、モデルの一部、リストやDictionaryなどの集合である可能性があります.
データがViewからControllerのActionに渡される場合、以下のようになります.
1.ルートデータ
2.QueryString(http getのクエリパラメータは?page=2など)
3.Forms(フォームpostのデータ)、
4.またはajaxインタラクションのjsonデータ.
ControllerのActionでは、これらのViewが渡すデータを常に取得し、Actionが望むタイプにバインドすることが望ましい.これらのタイプは、Actionのベースタイプパラメータであるか、更新する必要があるModelであるか、Modelの一部のみを更新するか、リストやDictionaryのようないくつかの集合の構造である可能性があります.
以下に、ViewTransferのデータを取得する方法をいくつか挙げます.
方法1.
public ActionResult Create()
{
    Recipe recipe = new Recipe();
    recipe.Name = Request.Form["Name"];
    // ...
   return View();
}
方法2.
public ActionResult Create(FormCollectionvalues)
{
    Recipe recipe = new Recipe();
    recipe.Name = values["Name"];       
    // ...
   return View();
}
方法3.
Asp.Net Mvcビルド機能(DefaultModelBinder)は、単純タイプ、複雑タイプ、集合タイプ、辞書タイプの自動バインドを実現します. 
1.単純タイプ
次のBookクラスを単純なタイプと呼びます.
<pre name="code" class="csharp"><pre name="code" class="csharp">public class Book
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public string Author { get; set; }
        public DateTime PublishedDate { get; set; }
    }

public ActionResult Create(Book book) {
            //TO DO
            //Insert book into Database
            return RedirectToAction("Index");
        }
 
 
 
 

对应的View页面代码如下:

<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("BookName")%>
        </div>
        <div>
            Author: <%=Html.TextBox("Author")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("PublishedDate")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>

注意TextBoxのnameは、バインドタイプに対応するPropertyName(大文字と小文字を区別しない)である必要があります.これにより、ページフォームsubmitの後、BookControllerの「Create」アクションで自動的にバインドされたbookオブジェクトを得ることができます.ここ、Asp.Net Mvcは、TextBoxのnameに変数名の接頭辞を付ける場合もサポートします.
<pre name="code" class="html"><pre name="code" class="html"><div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("book.BookName")%>
        </div>
        <div>
            Author: <%=Html.TextBox("book.Author")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("book.PublishedDate")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>
 
 
 
 

2. 复杂类型

现在对Book类作些许改动,并引入Author类

public class Book
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public Author Author { get; set; }
        public DateTime PublishedDate { get; set; }
    }

    public class Author {
        public int AuthorId { get; set; }
        public string AuthorName { get; set; }
        public string Nation { get; set; }
    }

対応するViewページのコードは次のとおりです.
<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("Author.AuthorName")%>
        </div>
        <div>
            Author's Nation: <%=Html.TextBox("Author.Nation")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>

3.集合タイプ
問題の複雑化を避けるために、元の簡単なBookタイプに戻ります.
<span style="font-size:12px;">public class Book
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public string Author { get; set; }
        public DateTime PublishedDate { get; set; }
    }</span><span style="font-size: 14px;">
</span>

次に、BookControllerの「Create」アクションをIListを受信するパラメータに変更します.
public ActionResult Create(IList<Book> books) {
            //TO DO
            //Insert book into Database
            return RedirectToAction("Index");
        }

Viewでは、IListタイプを自動的にバインドするために、次の命名規則を使用します.
<div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book Name: <%=Html.TextBox("books[0].BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[0].PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[0].Author")%>
        </div>
        <div>
            Book Name: <%=Html.TextBox("books[1].BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[1].PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[1].Author")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>

Actionの変数名「books」に接頭辞として括弧とインデックスを付け、インデックスは0から開始し、連続しなければならないというルールが表示されます.この命名規則により、IList、ICollection、IEnumerable、List、Book[]など、任意の集合タイプをバインドできます.
4.辞書の種類
単純なBookタイプを例にとり、「Create」アクションを受信IDictionaryタイプに変更しました.
public ActionResult Create(IDictionary<string, Book> books) {
            //TO DO
            //Insert book into Database
            return RedirectToAction("Index");
        }
対応するViewページのコードは次のとおりです.
<pre name="code" class="html"><pre name="code" class="html"><pre name="code" class="html"><div>
        <%using (Html.BeginForm("Create", "Book")) { %>
        <div>
            Book SN: <%=Html.TextBox("books[0].Key") %>
        </div>
        <div>
            Book Name: <%=Html.TextBox("books[0].Value.BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[0].Value.PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[0].Value.Author")%>
        </div>
        <div>
            Book SN: <%=Html.TextBox("books[1].Key") %>
        </div>
        <div>
            Book Name: <%=Html.TextBox("books[1].Value.BookName")%>
        </div>
        <div>
            Published Date: <%=Html.TextBox("books[1].Value.PublishedDate")%>
        </div>
        <div>
            Author's Name: <%=Html.TextBox("books[1].Value.Author")%>
        </div>
        <div>
            <input type="submit" id="submit" name="submit" value="submit" />
        </div>
        <%} %>
    </div>
 
  
 
  

可以看出,对于IDictioinary<Book>类型,在命名规则上,相比于集合类型,多了"Key"和"Value”的字眼。另,此规则也适用于Action参数类型为Dictionary<Book>的情形。简单类型、复杂类型、集合类型,以及字典类型 还能混合着用,而且绑定的数据来源也不局限于Form提交的表达数据,还可以是RouteData(url中的路由数据),QueryString(http get的查询参数如?page=2),或者ajax交互的json数据。

所以,只要我们遵循一定的命名规则,灵活的运用DefaultModelBinder 就可以轻松实现各种类型的自动绑定了。 

5.TryUpdateModel/UpdateModel  

 Mvc中经常遇到的一个典型用例是View通过From或者Ajax提交数据更新相应的Model,或者更新Model的部分内容,并且需要检查提交的数据对于Model的数据约束是否有效。这个时候TryUpdateModel就有用武之地了,它可以设置需要更新模型属性列表的(白名单)或要排除属性的列表(黑名单) 先看更新相关模型的所有属性:

publicActionResult Edit(int id, FormCollection collection)
{
    var oldData = _r.GetSingleData(id);
 
    if(TryUpdateModel(oldData, collection.AllKeys))
    {
        _r.Save();
    }
}

ID、Name以外のModel属性を更新するには:
publicActionResult Edit(Guid id, FormCollection collection)
{
    var oldData = _r.GetSingleData(id);
 
    if(TryUpdateModel(oldData,"", collection.AllKeys,newstring[]{"ID","Name"}))
    {
        _r.Save();
    }
}
publicActionResult Save()
{
    Customer customer =newCustomer();
   try   {
        UpdateModel(customer,new[] {"Name","Email",
           "Phone","Deposit"});
       return RedirectToAction("...");
    }
   catch(InvalidOperationException)
    {
       returnView(customer);
    }
}

UpdateModelとTryUpdateModelには、さまざまな柔軟な運用のために多くのリロード形式があり、モデルバインドデータにエラーがあるかどうかを自動的にチェックし、エラーがあれば自動的にModelStateに追加し、ページにヒントを与えます.
参照先:https://msdn.microsoft.com/zh-cn/library/system.web.mvc.controller_methods(v=vs.118).aspx
MVC学習シリーズ-Urlのルーティングルールを理解する
MVC学習シリーズ-HTMLヘルプの使用
MVCラーニングシリーズ-フォームの非同期コミット
MVCラーニングシリーズ-ViewDataとViewBag
MVCラーニングシリーズ-フォームデータの検証