Учебники

ASP.NET MVC – аннотации данных

DataAnnotations используется для настройки классов вашей модели, которые будут выделять наиболее часто используемые конфигурации. Аннотации данных также понимаются рядом приложений .NET, таких как ASP.NET MVC, что позволяет этим приложениям использовать одни и те же аннотации для проверки на стороне клиента. Атрибуты DataAnnotation переопределяют стандартные соглашения Code-First.

System.ComponentModel.DataAnnotations включает следующие атрибуты, которые влияют на обнуляемость или размер столбца.

  • ключ
  • Отметка
  • ConcurrencyCheck
  • необходимые
  • MinLength
  • Максимальная длина
  • StringLength

Пространство имен System.ComponentModel.DataAnnotations.Schema содержит следующие атрибуты, которые влияют на схему базы данных.

  • Таблица
  • колонка
  • Индекс
  • Иностранный ключ
  • NotMapped
  • InverseProperty

ключ

Entity Framework опирается на каждый объект, имеющий значение ключа, которое он использует для отслеживания объектов. Одно из соглашений, от которых зависит Code First, заключается в том, как оно подразумевает, какое свойство является ключом в каждом из классов Code First.

Соглашение заключается в том, чтобы искать свойство с именем «Id» или свойство, которое объединяет имя класса и «Id», например «StudentId». Свойство будет сопоставлено столбцу первичного ключа в базе данных. Классы Student, Course и Enrollment следуют этой конвенции.

Теперь давайте предположим, что класс Student использовал имя StdntID вместо ID. Если Code First не найдет свойство, соответствующее этому соглашению, оно выдаст исключение из-за требования Entity Framework о том, что у вас должно быть ключевое свойство.

Вы можете использовать аннотацию ключа, чтобы указать, какое свойство будет использоваться в качестве EntityKey.

Давайте посмотрим на класс Student, который содержит StdntID. Он не следует стандартному соглашению Code First, поэтому для обработки добавлен атрибут Key, который сделает его первичным ключом.

public class Student{
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Когда вы запустите приложение и загляните в базу данных в SQL Server Explorer, вы увидите, что первичным ключом теперь является StdntID в таблице учеников.

Первичный ключ StdntID

Entity Framework также поддерживает составные ключи. Составные ключи – это первичные ключи, которые состоят из нескольких свойств. Например, у вас есть класс DrivingLicense, первичный ключ которого представляет собой комбинацию LicenseNumber и IssuingCountry.

public class DrivingLicense{
   [Key, Column(Order = 1)]
   public int LicenseNumber { get; set; }
	
   [Key, Column(Order = 2)]
   public string IssuingCountry { get; set; }
   public DateTime Issued { get; set; }
   public DateTime Expires { get; set; }
}

Когда у вас есть составные ключи, Entity Framework требует, чтобы вы определили порядок свойств ключа. Вы можете сделать это, используя аннотацию Column для указания порядка.

Композитные ключи

Отметка

Code First будет обрабатывать свойства Timestamp так же, как свойства ConcurrencyCheck, но также будет гарантировать, что поле базы данных, сгенерированное Code First, не будет обнуляться.

Для проверки параллелизма чаще используются поля строк и меток времени. Но вместо использования аннотации ConcurrencyCheck вы можете использовать более конкретную аннотацию TimeStamp, если типом свойства является байтовый массив. Вы можете иметь только одно свойство отметки времени в данном классе.

Давайте рассмотрим простой пример, добавив свойство TimeStamp в класс Course.

public class Course{
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
   [Timestamp]
   public byte[] TStamp { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Как видно из приведенного выше примера, атрибут Timestamp применяется к свойству Byte [] класса Course. Итак, Code First создаст столбец отметки времени TStamp в таблице Courses.

ConcurrencyCheck

Аннотация ConcurrencyCheck позволяет пометить одно или несколько свойств, которые будут использоваться для проверки параллелизма в базе данных, когда пользователь редактирует или удаляет объект. Если вы работали с EF Designer, это соответствует настройке свойства ConcurrencyMode свойства Fixed.

Давайте рассмотрим простой пример и посмотрим, как работает ConcurrencyCheck, добавив его в свойство Title в классе Course.

public class Course{
   public int CourseID { get; set; }
	
   [ConcurrencyCheck]
   public string Title { get; set; }
   public int Credits { get; set; }
	
   [Timestamp, DataType("timestamp")]
   public byte[] TimeStamp { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

В приведенном выше классе Course атрибут ConcurrencyCheck применяется к существующему свойству Title. Code First будет включать столбец Title в команде update для проверки оптимистичного параллелизма, как показано в следующем коде.

exec sp_executesql N'UPDATE [dbo].[Courses]
   SET [Title] = @0
   WHERE (([CourseID] = @1) AND ([Title] = @2))
   ',N'@0 nvarchar(max) ,@1 int,@2 nvarchar(max)
',@0 = N'Maths',@1 = 1,@2 = N'Calculus'
go

необходимые

Обязательная аннотация сообщает EF, что требуется определенное свойство. Давайте посмотрим на следующий класс Student, в котором обязательный идентификатор добавлен в свойство FirstMidName. Обязательный атрибут заставит EF убедиться, что свойство содержит данные.

public class Student{
   [Key]
   public int StdntID { get; set; }
	
   [Required]
   public string LastName { get; set; }
	
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Вы можете видеть в приведенном выше примере Атрибут Обязательный класс ученика применяется к FirstMidName и LastName. Таким образом, Code First создаст столбцы NOT NULL FirstMidName и LastName в таблице учеников, как показано на следующем снимке экрана.

Стол для студентов

Максимальная длина

Атрибут MaxLength позволяет указать дополнительные проверки свойств. Его можно применять к свойству типа строки или массива класса домена. EF Code First установит размер столбца, как указано в атрибуте MaxLength.

Давайте посмотрим на следующий класс Course, в котором атрибут MaxLength (24) применяется к свойству Title.

public class Course{
   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24)]
	
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Когда вы запустите указанное выше приложение, Code-First создаст заголовок столбца nvarchar (24) в таблице Coursed, как показано на следующем снимке экрана.

Заголовок колонки

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

MinLength

Атрибут MinLength позволяет вам указать дополнительные проверки свойств, как вы это делали с MaxLength. Атрибут MinLength также можно использовать с атрибутом MaxLength, как показано в следующем коде.

public class Course{
   public int CourseID { get; set; }
   [ConcurrencyCheck]
   [MaxLength(24) , MinLength(5)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

EF выбросит EntityValidationError, если вы установите значение свойства Title меньше указанной длины в атрибуте MinLength или больше указанной длины в атрибуте MaxLength.

StringLength

StringLength также позволяет указывать дополнительные проверки свойств, такие как MaxLength. Разница в том, что атрибут StringLength может применяться только к свойству строкового типа классов Domain.

public class Course{
   public int CourseID { get; set; }
   [StringLength (24)]
   public string Title { get; set; }
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Entity Framework также проверяет значение свойства для атрибута StringLength. Теперь, если пользователь устанавливает заголовок, который содержит более 24 символов, EF сгенерирует EntityValidationError.

Таблица

Код по умолчанию Первое соглашение создает имя таблицы, такое же, как имя класса. Если вы разрешаете Code First создать базу данных, вы также можете изменить имя создаваемых таблиц. Вы можете использовать Code First с существующей базой данных. Но это не всегда тот случай, когда имена классов соответствуют именам таблиц в вашей базе данных.

Атрибут таблицы переопределяет это соглашение по умолчанию. EF Code First создаст таблицу с указанным именем в атрибуте Table для данного класса домена.

Давайте рассмотрим пример, в котором класс называется Student, и, по соглашению, Code First предполагает, что он будет сопоставлен с таблицей с именем Students. Если это не так, вы можете указать имя таблицы с атрибутом Table, как показано в следующем коде.

[Table("StudentsInfo")]
public class Student{
   [Key]
   public int StdntID { get; set; }
	
   [Required]
   public string LastName { get; set; }
	
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Теперь вы можете видеть, что атрибут Table определяет таблицу как StudentsInfo. Когда таблица будет сгенерирована, вы увидите имя таблицы StudentsInfo, как показано на следующем снимке экрана.

Название таблицы StudentsInfo

Вы не можете указать только имя таблицы, но вы также можете указать схему для таблицы, используя атрибут Table, используя следующий код.

[Table("StudentsInfo", Schema = "Admin")]

public class Student{
   [Key]
   public int StdntID { get; set; }
	
   [Required]
   public string LastName { get; set; }
	
   [Required]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

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

Таблица StudentsInfo в схеме администратора

колонка

Он также совпадает с атрибутом таблицы, но атрибут таблицы переопределяет поведение таблицы, а атрибут столбца переопределяет поведение столбца. Код по умолчанию Первое соглашение создает имя столбца, такое же, как имя свойства.

Если вы позволяете Code First создать базу данных, а также хотите изменить имя столбцов в ваших таблицах. Атрибут столбца переопределяет это соглашение по умолчанию. EF Code First создаст столбец с указанным именем в атрибуте Column для данного свойства.

Давайте еще раз посмотрим на следующий пример, в котором свойство называется FirstMidName, и, по соглашению, Code First предполагает, что это сопоставит столбец с именем FirstMidName. Если это не так, вы можете указать имя столбца с атрибутом Column, как показано в следующем коде.

public class Student{
   public int ID { get; set; }
   public string LastName { get; set; }
	
   [Column("FirstName")]
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Теперь вы можете видеть, что атрибут Column определяет столбец как FirstName. Когда таблица будет сгенерирована, вы увидите имя столбца FirstName, как показано на следующем снимке экрана.

Имя столбца Имя

Индекс

Атрибут Index был введен в Entity Framework 6.1. Примечание. Если вы используете более раннюю версию, информация в этом разделе не применяется.

Вы можете создать индекс для одного или нескольких столбцов, используя IndexAttribute. Добавление атрибута к одному или нескольким свойствам приведет к тому, что EF создаст соответствующий индекс в базе данных при создании базы данных.

Индексы делают поиск данных быстрее и эффективнее, в большинстве случаев. Однако перегрузка таблицы или представления индексами может неприятно повлиять на производительность других операций, таких как вставки или обновления.

Индексирование – это новая функция в Entity Framework, позволяющая повысить производительность приложения Code First, сократив время, необходимое для запроса данных из базы данных.

Вы можете добавить индексы в свою базу данных, используя атрибут Index, и переопределить параметры по умолчанию Unique и Clustered, чтобы получить индекс, наиболее подходящий для вашего сценария. По умолчанию индекс будет иметь имя IX_ <имя свойства>

Давайте посмотрим на следующий код, в котором атрибут Index добавлен в класс Course for Credits.

public class Cours{
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Вы можете видеть, что атрибут Index применяется к свойству Credits. Теперь, когда таблица сгенерирована, вы увидите IX_Credits в индексах.

IX_Credits в индексах

По умолчанию индексы не являются уникальными, но вы можете использовать именованный параметр IsUnique, чтобы указать, что индекс должен быть уникальным. В следующем примере представлен уникальный индекс, как показано в следующем коде.

public class Course{
   public int CourseID { get; set; }
   [Index(IsUnique = true)]
	
   public string Title { get; set; }
   [Index]
	
   public int Credits { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Иностранный ключ

Соглашение Code First позаботится о наиболее распространенных отношениях в вашей модели, но в некоторых случаях требуется помощь. Например, при изменении имени свойства ключа в классе Student возникла проблема с его отношением к классу Enrollment.

public class Enrollment{
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}

public class Student{
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

При создании базы данных Code First видит свойство StudentID в классе Enrollment и распознает его по соглашению, что оно соответствует имени класса плюс «ID», как внешний ключ к классу Student. Но в классе Student отсутствует свойство StudentID, а в классе Student это свойство StdntID.

Решением для этого является создание свойства навигации в Enrollment и использование ForeignKey DataAnnotation, чтобы помочь Code First понять, как построить отношения между двумя классами, как показано в следующем коде.

public class Enrollment{
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
   public virtual Course Course { get; set; }
	
   [ForeignKey("StudentID")]
   public virtual Student Student { get; set; }
}

Теперь вы можете видеть, что атрибут ForeignKey применяется к свойству навигации.

Атрибут ForeignKey

NotMapped

По умолчанию соглашения Code First, каждое свойство, которое имеет поддерживаемый тип данных и которое включает в себя геттеры и сеттеры, представлены в базе данных. Но это не всегда так в приложениях. Атрибут NotMapped переопределяет это соглашение по умолчанию. Например, у вас может быть свойство в классе Student, такое как FatherName, но его не нужно хранить. Вы можете применить атрибут NotMapped к свойству FatherName, для которого вы не хотите создавать столбец в базе данных. Ниже приведен код.

public class Student{
   [Key]
   public int StdntID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
   [NotMapped]
   public int FatherName { get; set; }

   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Вы можете видеть, что атрибут NotMapped применяется к свойству FatherName. Теперь, когда таблица сгенерирована, вы увидите, что столбец FatherName не будет создан в базе данных, но присутствует в классе Student.

Колонка FatherName создана

Code First не создаст столбец для свойства, в котором нет ни методов получения, ни установки.

InverseProperty

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

Давайте добавим два свойства навигации для класса Enrollment.

public class Enrollment{
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course CurrCourse { get; set; }
   public virtual Course PrevCourse { get; set; }
   public virtual Student Student { get; set; }
}

Точно так же вам также нужно добавить в класс Course, на который ссылаются эти свойства. Класс Course имеет свойства навигации обратно к классу Enrollment, который содержит все текущие и предыдущие записи.

public class Course{
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
	
   public int Credits { get; set; }
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

Code First создает столбец внешнего ключа {Class Name} _ {Primary Key}, если свойство внешнего ключа не включено в определенный класс, как показано в приведенных выше классах. Когда база данных сгенерирована, вы увидите несколько внешних ключей, как показано на следующем снимке экрана.

Количество иностранных ключей

Как вы можете видеть, Code First не может сопоставить свойства двух классов самостоятельно. Таблица базы данных для заявок должна иметь один внешний ключ для CurrCourse и один для PrevCourse, но Code First создаст четыре свойства внешнего ключа, т.е.

  • CurrCourse_CourseID
  • PrevCourse_CourseID
  • Course_CourseID
  • Course_CourseID1

Чтобы исправить эти проблемы, вы можете использовать аннотацию InverseProperty, чтобы указать выравнивание свойств.

public class Course{
   public int CourseID { get; set; }
   public string Title { get; set; }
	
   [Index]
   public int Credits { get; set; }
	
   [InverseProperty("CurrCourse")]
   public virtual ICollection<Enrollment> CurrEnrollments { get; set; }
	
   [InverseProperty("PrevCourse")]
   public virtual ICollection<Enrollment> PrevEnrollments { get; set; }
}

Как вы можете видеть теперь, когда атрибут InverseProperty применяется в указанном выше классе Course путем указания, к какому ссылочному свойству класса Enrollment он принадлежит, Code First создаст базу данных и создаст только два столбца внешнего ключа в таблице Enrollments, как показано на следующем снимке экрана.

Таблица регистрации иностранных языков

Мы рекомендуем вам выполнить приведенный выше пример для лучшего понимания.