Проверка с помощью интерфейса IDataErrorInfo (C#)

Стивен Уолтер (Stephen Walther)

Стивен Уолтер показывает, как отображать пользовательские сообщения об ошибках проверки, реализовав интерфейс IDataErrorInfo в классе модели.

Цель этого руководства — объяснить один из подходов к выполнению проверки в ASP.NET приложении MVC. Вы узнаете, как запретить пользователям отправлять HTML-форму, не предоставляя значения для обязательных полей формы. В этом руководстве описано, как выполнить проверку с помощью интерфейса IErrorDataInfo.

Предположения

В этом руководстве мы будем использовать базу данных MoviesDB и таблицу базы данных Movies. Эта таблица имеет следующие столбцы.

Имя столбца Тип данных Разрешить значения NULL
Идентификатор Int Неверно
Заголовок Nvarchar(100) Неверно
Директор Nvarchar(100) Неверно
Дата, дата, датасложенная Дата и время Неверно

В этом руководстве я использую Microsoft Entity Framework для создания классов модели базы данных. Класс Movie, созданный Entity Framework, показан на рис. 1.

Сущность Movie

Рис. 01. Сущность Movie (щелкните для просмотра полноразмерного изображения)

Примечание

Дополнительные сведения об использовании Entity Framework для создания классов модели базы данных см. в руководстве Создание классов моделей с помощью Entity Framework.

Класс контроллера

Мы используем контроллер 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();
            }
        }

    }
}

Класс контроллера Home в листинге 1 содержит два действия Create(). Первое действие отображает HTML-форму для создания нового фильма. Второе действие Create() выполняет фактическую вставку нового фильма в базу данных. Второе действие Create() вызывается при отправке на сервер формы, отображаемой первым действием Create().

Обратите внимание, что второе действие Create() содержит следующие строки кода:

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

Свойство IsValid возвращает значение false при возникновении ошибки проверки. В этом случае представление "Создать", содержащее HTML-форму для создания фильма, переигрывается.

Создание разделяемого класса

Класс Movie создается платформой Entity Framework. Код класса Movie можно просмотреть, если развернуть файл MoviesDBModel.edmx в окне Обозреватель решений и открыть MoviesDBModel.Designer. CS-файл в редакторе кода (см. рис. 2).

Код для сущности Movie

Рис. 02. Код для сущности Movie(Щелкните для просмотра полноразмерного изображения)

Класс Movie является разделяемым классом. Это означает, что мы можем добавить еще один разделяемый класс с тем же именем, чтобы расширить функциональные возможности класса Movie. Мы добавим логику проверки в новый разделяемый класс.

Добавьте класс из листинга 2 в папку Models.

Листинг 2. Models\Movie.cs

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

namespace MvcApplication1.Models
{

    public partial class Movie 
    {

    }
}

Обратите внимание, что класс в листинге 2 содержит модификатор partial . Все методы или свойства, добавленные в этот класс, становятся частью класса Movie, созданного Entity Framework.

Добавление разделяемых методов OnChanging и OnChanged

Когда Entity Framework создает класс сущности, Entity Framework автоматически добавляет разделяемые методы в класс. Entity Framework создает разделяемые методы OnChanging и OnChanged, соответствующие каждому свойству класса .

В случае с классом Movie платформа Entity Framework создает следующие методы:

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

Метод OnChanging вызывается непосредственно перед изменением соответствующего свойства. Метод OnChanged вызывается сразу после изменения свойства .

Вы можете воспользоваться преимуществами этих частичных методов, чтобы добавить логику проверки в класс Movie. Класс update Movie в листинге 3 проверяет, назначены ли свойствам 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 назначается сообщение об ошибке.

На этом этапе ничего не происходит при назначении пустой строки свойству 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 будет использовать этот интерфейс при создании экземпляра класса . Например, действие Create() контроллера Home принимает экземпляр класса 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 создает экземпляр действия Movie, переданного в Create(), с помощью связывателя модели (DefaultModelBinder). Связыватель модели отвечает за создание экземпляра объекта Movie путем привязки полей ФОРМЫ HTML к экземпляру объекта Movie.

DefaultModelBinder определяет, реализует ли класс интерфейс IDataErrorInfo. Если класс реализует этот интерфейс, связыватель модели вызывает индексатор IDataErrorInfo.this для каждого свойства класса. Если индексатор возвращает сообщение об ошибке, связыватель модели автоматически добавляет это сообщение об ошибке в состояние модели.

DefaultModelBinder также проверяет свойство IDataErrorInfo.Error. Это свойство предназначено для представления ошибок проверки, не относящихся к свойствам, связанных с классом . Например, может потребоваться применить правило проверки, которое зависит от значений нескольких свойств класса Movie. В этом случае вы вернете ошибку проверки из свойства Error.

Обновленный класс Movie в листинге 4 реализует интерфейс 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, показано, что происходит, если не ввести значения для полей формы "Название" или "Директор".

Автоматическое создание методов действий

Рис. 03. Форма с отсутствующими значениями (щелкните для просмотра полноразмерного изображения)

Обратите внимание, что значение DateReleased проверяется автоматически. Так как свойство DateReleased не принимает значения NULL, DefaultModelBinder автоматически создает ошибку проверки для этого свойства, если оно не имеет значения. Если вы хотите изменить сообщение об ошибке для свойства DateReleased, необходимо создать настраиваемый связыватель модели.

Итоги

В этом руководстве вы узнали, как использовать интерфейс IDataErrorInfo для создания сообщений об ошибках проверки. Сначала мы создали разделяемый класс Movie, который расширяет функциональные возможности разделяемого класса Movie, созданного Entity Framework. Далее мы добавили логику проверки в разделяемые методы Класса Movie OnTitleChanging() и OnDirectorChanging(). Наконец, мы реализовали интерфейс IDataErrorInfo, чтобы предоставить эти сообщения проверки платформе ASP.NET MVC.