共用方式為


驗證與 IDataErrorInfo 介面 (C#)

作者:Stephen Walther

Stephen Walther 示範如何在模型類別中實作 IDataErrorInfo 介面,以顯示自訂驗證錯誤訊息。

本教學課程的目標是說明在 ASP.NET MVC 應用程式中執行驗證的一種方法。 您將瞭解如何防止某人提交 HTML 表單,而不提供必要表單欄位的值。 在本教學課程中,您將瞭解如何使用 IErrorDataInfo 介面來執行驗證。

假設

在本教學課程中,我將使用 MoviesDB 資料庫和 Movies 資料庫資料表。 這個資料表具有下列資料行:

資料行名稱 資料類型 允許 Null
Id Int
標題 NVarchar (100)
導演 NVarchar (100)
DateReleased Datetime

在本教學課程中,我使用 Microsoft Entity Framework 來產生我的資料庫模型類別。 Entity Framework 所產生的 Movie 類別會顯示在圖 1 中。

Movie 實體

圖 01:電影實體 (按一下即可檢視全大小的影像)

注意

若要深入瞭解如何使用 Entity Framework 來產生資料庫模型類別,請參閱我的教學課程主題為使用 Entity Framework 建立模型類別。

Controller 類別

我們使用 Home 控制器來列出電影並建立新的電影。 此類別的程式碼包含在清單 1 中。

清單 1 - Controllers\HomeController.cs

using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;

namespace MvcApplication1.Controllers
{

    public class HomeController : Controller
    {
        private MoviesDBEntities _db = new MoviesDBEntities();

        public ActionResult Index()
        {
            return View(_db.MovieSet.ToList());
        }

        public ActionResult Create()
        {
            return View();
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
        {
            // Validate
            if (!ModelState.IsValid)
                return View();

            // Add to database
            try
            {
                _db.AddToMovieSet(movieToCreate);
                _db.SaveChanges();

                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

    }
}

清單 1 中的 Home 控制器類別包含兩個 Create () 動作。 第一個動作會顯示建立新電影的 HTML 表單。 第二個 Create () 巨集指令會執行新電影的實際插入資料庫中。 第二個 Create () 巨集指令會在第一個 Create () 巨集指令所顯示的表單提交至伺服器時叫用。

請注意,第二個 Create () 巨集指令包含下列幾行程式碼:

// Validate
if (!ModelState.IsValid)
    return View();

IsValid 屬性會在發生驗證錯誤時傳回 false。 在此情況下,會重新顯示包含建立電影之 HTML 表單的 [建立] 檢視。

建立部分類別

Movie 類別是由 Entity Framework 產生。 如果您在 [方案總管] 視窗中展開 MoviesDBModel.edmx 檔案,並開啟 MoviesDBModel.Designer,則可以看到 MoviesDB 類別的程式碼。程式碼編輯器中的 cs 檔案 (請參閱圖 2) 。

Movie 實體的程式碼

圖 02:影片實體的程式碼 (按一下即可檢視全大小影像)

Movie 類別是部分類別。 這表示我們可以新增另一個具有相同名稱的部分類別,以擴充 Movie 類別的功能。 我們會將驗證邏輯新增至新的部分類別。

將清單 2 中的 類別新增至 Models 資料夾。

清單 2 - Models\Movie.cs

using System.Collections.Generic;
using System.ComponentModel;

namespace MvcApplication1.Models
{

    public partial class Movie 
    {

    }
}

請注意,清單 2 中的 類別包含 部分 修飾詞。 您新增至此類別的任何方法或屬性都會成為 Entity Framework 所產生的 Movie 類別的一部分。

新增 OnChanging 和 OnChanged 部分方法

當 Entity Framework 產生實體類別時,Entity Framework 會自動將部分方法新增至 類別。 Entity Framework 會產生 OnChanging 和 OnChanged 部分方法,這些方法會對應至類別的每個屬性。

在 Movie 類別的情況下,Entity Framework 會建立下列方法:

  • OnIdChanging
  • OnIdChanged
  • OnTitleChanging
  • OnTitleChanged
  • OnDirectorChanging
  • OnDirectorChanged
  • OnDateReleasedChanging
  • OnDateReleasedChanged

在對應的屬性變更之前,會立即呼叫 OnChanging 方法。 屬性變更之後,會立即呼叫 OnChanged 方法。

您可以利用這些部分方法,將驗證邏輯新增至 Movie 類別。 清單 3 中的 update Movie 類別會確認 Title 和 Director 屬性已指派無空值。

注意

部分方法是在您不需要實作的類別中定義的方法。 如果您未實作部分方法,編譯器會移除方法簽章和方法的所有呼叫,因此沒有與部分方法相關聯的執行時間成本。 在 [Visual Studio Code編輯器] 中,您可以輸入關鍵字partial,後面接著空格來檢視要實作的部分清單,以新增部分方法。

清單 3 - Models\Movie.cs

using System.Collections.Generic;
using System.ComponentModel;

namespace MvcApplication1.Models
{

    public partial class Movie : IDataErrorInfo
    {
        private Dictionary<string, string> _errors = new Dictionary<string, string>();

        partial void OnTitleChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Title", "Title is required.");
        }

        partial void OnDirectorChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Director", "Director is required.");
        }

    }
}

例如,如果您嘗試將空字串指派給 Title 屬性,則會將錯誤訊息指派給名為 _errors 的 Dictionary。

此時,當您將空字串指派給 Title 屬性,並將錯誤新增至私用_errors欄位時,實際上不會發生任何事。 我們需要實作 IDataErrorInfo 介面,將這些驗證錯誤公開給 ASP.NET MVC 架構。

實作 IDataErrorInfo 介面

自第一版起,IDataErrorInfo 介面已屬於 .NET Framework 的一部分。 此介面是非常簡單的介面:

public interface IDataErrorInfo
{
    string this[string columnName] { get; }

    string Error { get; }
}

如果類別實作 IDataErrorInfo 介面,ASP.NET MVC 架構會在建立 類別的實例時使用此介面。 例如,Home controller Create () 巨集指令會接受 Movie 類別的實例:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Movie movieToCreate)
{
    // Validate
    if (!ModelState.IsValid)
        return View();

    // Add to database
    try
    {
        _db.AddToMovieSet(movieToCreate);
        _db.SaveChanges();

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

ASP.NET MVC 架構會使用模型系結器 (DefaultModelBinder) ,建立傳遞至 Create () 巨集指令的 Movie 實例。 模型系結器負責建立 Movie 物件的實例,方法是將 HTML 表單欄位系結至 Movie 物件的實例。

DefaultModelBinder 會偵測類別是否實作 IDataErrorInfo 介面。 如果類別實作這個介面,模型系結器會針對類別的每個屬性叫用 IDataErrorInfo.this 索引子。 如果索引子傳回錯誤訊息,模型系結器會自動將此錯誤訊息新增至模型狀態。

DefaultModelBinder 也會檢查 IDataErrorInfo.Error 屬性。 這個屬性的目的是代表與 類別相關聯的非屬性特定驗證錯誤。 例如,您可能想要強制執行驗證規則,其取決於 Movie 類別之多個屬性的值。 在此情況下,您會從 Error 屬性傳回驗證錯誤。

清單 4 中更新的 Movie 類別會實作 IDataErrorInfo 介面。

清單 4 - Models\Movie.cs (會實作 IDataErrorInfo)

using System.Collections.Generic;
using System.ComponentModel;

namespace MvcApplication1.Models
{

    public partial class Movie : IDataErrorInfo
    {
        private Dictionary<string, string> _errors = new Dictionary<string, string>();

        partial void OnTitleChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Title", "Title is required.");
        }

        partial void OnDirectorChanging(string value)
        {
            if (value.Trim().Length == 0)
                _errors.Add("Director", "Director is required.");
        }

        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                return string.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                if (_errors.ContainsKey(columnName))
                    return _errors[columnName];
                return string.Empty;
            }
        }

        #endregion
    }
}

在清單 4 中,索引子屬性會檢查_errors集合,以查看它是否包含對應至索引子之屬性名稱的索引鍵。 如果沒有與 屬性相關聯的驗證錯誤,則會傳回空字串。

您不需要以任何方式修改 Home 控制器,即可使用修改過的 Movie 類別。 圖 3 中顯示的頁面說明當 [標題] 或 [Director] 表單欄位未輸入任何值時,會發生什麼情況。

自動建立動作方法

圖 03按一下即可檢視大小完整影像 () 遺漏值的表單

請注意,DateReleased 值會自動驗證。 因為 DateReleased 屬性不接受 Null 值,所以 DefaultModelBinder 會在此屬性沒有值時自動產生這個屬性的驗證錯誤。 如果您想要修改 DateReleased 屬性的錯誤訊息,則需要建立自訂模型系結器。

總結

在本教學課程中,您已瞭解如何使用 IDataErrorInfo 介面來產生驗證錯誤訊息。 首先,我們建立了部分 Movie 類別,擴充 Entity Framework 所產生的部分 Movie 類別的功能。 接下來,我們已將驗證邏輯新增至 Movie 類別 OnTitleChanging () 和 OnDirectorChanging () 部分方法。 最後,我們實作 IDataErrorInfo 介面,以便將這些驗證訊息公開至 ASP.NET MVC 架構。