company_banner

Учебный курс. Создание сложной модели данных для приложения ASP.NET MVC, часть 1

Автор оригинала: ASP.NET Team
  • Перевод
Это продложение цикла статей, посвященого разработке с помощью Entity Framework и ASP.NET MVC 3. Первые главы вы можете найти по следующим ссылкам:
В предыдущих уроках вы научились работать с простой моделью данных, состоящей из трёх сущностей. В этом уроке вы добавите несколько сущностей и связей между ними и научитесь работать с аннотациями для управления классами моделей.

Результат будет выглядеть так:

image

Использование атрибутов для управления форматированием, валидацией и маппингом базы данных


В этом уроке вы увидите примеры атрибутов, которвые вы можете добавить в классы моделей для управления форматированием, валидацией и маппингом базы данных. Затем вы создадите полноценную модель данных School путём добавления атрибутов в уже созданные классы и созданием новых классов для оставшихся в модели типов сущностей.

Атрибут DisplayFormat


Для дат записи студентов, все страницы сейчас отображают время вместе с датой, тогда как нам нужна только дата. С помощью аннотаций можно указать в одном месте настройку, которая будет распространяться на всё. Для этого добавим атрибут к свойству EnrollmentDate в классе Student.

В Models\Student.cs добавьте using для System.ComponentModel.DataAnnotations и атрибут DisplayFormat для свойства EnrollmentDate:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
 
namespace ContosoUniversity.Models 
{ 
    public class Student 
    { 
        public int StudentID { get; set; } 
        public string LastName { get; set; } 

        public string FirstMidName { get; set; } 
 
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] 
        public DateTime EnrollmentDate { get; set; } 
 
        public virtual ICollection<Enrollment> Enrollments { get; set; } 
    } 
}

Строка форматирования указывает, что необходимо отображать только короткую форму даты этого свойства. Настройка ApplyFormatInEditMode указывает, что данное форматирование должно применяться также для значений, отображающихся в текстовых строках, предназначенных для редактирования. (допустим, нам не нужно отображать в некоторых полях, например, в которых содержатся значения, связанные с валютой, знак валюты)

Убедитесь, что на страницах About и Student время в датах больше не показывается.

image

Атрибут MaxLength


Атрибутами можно определить правила валидации, допустим, в том случае, если необходимо ограничить введённое пользователем значение 50-ю символами. Чтобы воспользоваться этой возможностью, добавьте атрибут Range в свойствам LastName и FirstMidName:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
 
namespace ContosoUniversity.Models 
{ 
    public class Student 
    { 
        public int StudentID { get; set; } 
 
        [MaxLength(50)] 
        public string LastName { get; set; } 
 
        [MaxLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")] 
        public string FirstMidName { get; set; } 
 
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] 
        public DateTime EnrollmentDate { get; set; } 
 
        public virtual ICollection<Enrollment> Enrollments { get; set; } 
    } 
}

При попытке ввести фамилию из более чем 50-ти символов будет выведено стандартное сообщение об ошибке, в подобном случае же для имени будет выведено наше собственное сообщение об ошибке.

Откройте страницу Create и попробуйте ввести два имени из более чем 50-ти символов, затем нажмите Create чтобы убедиться что валидация работает.

image

Указывать максимальное значение для вводимых элементов – одна из хороших практик. В ином случае, если используется подход Code First, при создании базы данных имена соответствующих столбцов будут иметь максимально допустимую длину.

Атрибут Column


Атрибутами можно контролировать маппинг классов на базу данных. Допустим, вы использовали имя FirstMidName для поля «Имя» потому, что это поле могло также содержать отчество. Но вы хотите, чтобы в базе данных соответствующий столбец назывался FirstName, потому что пользователи пользуются в своих запросах именно этим наименованием. Для этого можно воспользоваться атрибутом Column.

Атрибут Column указывает, что при создании базы данных, столбец таблицы Student, который маппится на свойство FirstMidName, будет называться FirstName.

Добавьте этот атрибут к свойству FirstMidName:

[Column("FirstName")]
public string FirstMidName { get; set; }

Откройте страницу Student Index, на которой ничего не изменилось (необходимо не просто запустить сайт и посмотреть на домашнюю страницу, необходимо перейти на страницу Student Index чтобы инициировать обращение к базе данных, что инициирует также ее удаление и пересоздание). Однако, если вы откроете базу данных ServerExplorer вы увидите, что в таблице Student столбец называется FirstName.

image

В окне Propertiesможно заметить, что длина ограничена 50-тью символами благодаря использованию атрибута MaxLength.

image

Создание сущности Instructor


image

Создайте Models\Instructor.csсо следующим кодом:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
 
namespace ContosoUniversity.Models 
{ 
    public class Instructor 
    { 
        public Int32 InstructorID { get; set; } 
 
        [Required(ErrorMessage = "Last name is required.")] 
        [Display(Name="Last Name")] 
        [MaxLength(50)] 
        public string LastName { get; set; } 
 
        [Required(ErrorMessage = "First name is required.")] 
        [Column("FirstName")] 
        [Display(Name = "First Name")] 
        [MaxLength(50)] 
        public string FirstMidName { get; set; } 
 
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)] 
        [Required(ErrorMessage = "Hire date is required.")] 
        [Display(Name = "Hire Date")] 
        public DateTime? HireDate { get; set; } 
 
        public string FullName  
        { 
            get 
            { 
                return LastName + ", " + FirstMidName; 
            } 
        } 
 
        public virtual ICollection<Course> Courses { get; set; } 
        public virtual OfficeAssignment OfficeAssignment { get; set; } 
    } 
}

Обратите внимание, что некоторые из свойств аналогичны свойствам сущности Student. В Implementing Inheritance мы используем наследование для решения проблемы избыточности.

Атрибуты Required и Display

Атрибуты свойства LastName указывают на то, что это необходимое для заполнения значение, заголовок для текстового поля должен быть равен “Last Name” (вместо названия свойства “LastName”) и длина введённого значения должна быть не более 50-ти символов..

[Required(ErrorMessage = "Last name is required.")] 
[Display(Name="Last Name")] 
[MaxLength(50)] 
public string LastName { get; set; }

Вычисляемое свойство FullName

FullName является вычисляемым свойством, которое содержит значение, вычисляемое путем соединения двух других свойств. Поэтому оно имеет только аксессор, в базе данных же не создаётся соответствующего столбца.

public string FullName  
{ 
    get 
    { 
        return LastName + ", " + FirstMidName; 
    } 
}

Navigation Properties Courses и OfficeAssignment

Свойства Courses и OfficeAssignment — navigation properties. Как было упомянуто ранее, они обычно помечаются модификатором virtual чтобы использовать lazy loading. Кроме этого, если данное свойство может содержать несколько сущностей, его тип должен быть Icollection.

Поскольку преподаватель может вести несколько курсов, переменная Courses определена как коллекция сущностей Course. С другой стороны, преподаватель может иметь только один офис, поэтому OfficeAsignment определн как единственная сущность типа OfficeAssignment (которая может быть равна null, если у преподавателя нет офиса).

public virtual ICollection<Course> Courses { get; set; } 
public virtual OfficeAssignment OfficeAssignment { get; set; }

Создание сущности OfficeAssignment


image

Создайте Models\OfficeAssignment.cs со следующим содержанием:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
 
namespace ContosoUniversity.Models 
{ 
    public class OfficeAssignment 
    { 
        [Key] 
        public int InstructorID { get; set; } 
 
        [MaxLength(50)] 
        [Display(Name = "Office Location")] 
        public string Location { get; set; } 
 
        public virtual Instructor Instructor { get; set; } 
    } 
}

Атрибут Key

Между сущностями Instructor и OfficeAssignment существует связь один-к-нулю-или-единице. Назначенный офис существует только в сочетании с назначенным преподавателем, поэтому его первичный ключ также является внешним ключом на сущность Instructor. Entity Framework, однако, не может автоматически распознать InstructorID как первичный ключ поскольку имя данного свойства не соответствует конвенциям именования первичных ключей, не равно ID и classnameID. Поэтому атрибутом Key указывается, что данный атрибут является первичным ключом.

[Key] 
public int InstructorID { get; set; }

Атрибут Key можно использовать также для той ситуации, когда у вас уже есть первичный ключ, но вы хотите назвать свойство иначе чем classnameID или ID.

Instructor Navigation Property

Сущность Instructor nullable OfficeAssignment navigation property (преподаватель может не иметь офиса), а сущность OfficeAssignment имеет не-nullable Instructor navigation property (офис не может существовать без преподавателя). Когда Instructor имеет ассоциированую с ним сущность OfficeAsignment, оба имеют друг на друга ссылки в своих navigation property.

Продолжение следует…

Благодарности


Благодарим за помощь в переводе Александра Белоцерковского (ahriman).
  • +22
  • 11,2k
  • 6
Microsoft
586,00
Microsoft — мировой лидер в области ПО и ИТ-услуг
Поделиться публикацией

Комментарии 6

    0
    [Required(ErrorMessage = "Last name is required.")] 
    [Display(Name="Last Name")] 
    [MaxLength(50)] 
    public string LastName { get; set; }


    Подскажите как происходит локализация такого кода?
    0
    Как решить проблему, когда одну и туже сущность в разных местах нужно отображать по разному? Например, дата в компоненте редактирования выглядит по одному, а в гриде с общим списком — по другому.
      +1
      ViewModel?
        0
        Я вырос на функциональном программировании. Слабо ориентируюсь в модных веяниях, потому ни ViewModel, ни MVVM мне ни о чем не говорят, расскажите лучше «на пальцах», как бы вы поступили для решения задачи?

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое