Учебники

Entity Framework — Аннотации данных

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

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

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

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

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

ключ

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

  • Соглашение заключается в поиске свойства с именем «Id» или свойства, которое объединяет имя класса и «Id», например «StudentId».

  • Свойство будет сопоставлено столбцу первичного ключа в базе данных.

  • Классы Student, Course и Enrollment следуют этой конвенции.

Соглашение заключается в поиске свойства с именем «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 в таблице студентов.

Основной ключ

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, но также будет гарантировать, что поле базы данных, которое код генерирует первым, не обнуляется.

  • Для проверки параллелизма чаще используются поля строк и меток времени.

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

  • Вы можете иметь только одно свойство отметки времени в данном классе.

Для проверки параллелизма чаще используются поля строк и меток времени.

Вместо того, чтобы использовать аннотацию 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; }
}

Как видно из приведенного выше примера, атрибут Required применяется к 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) в таблице CourseId, как показано на следующем рисунке.

Колонна Нварчар

Когда пользователь устанавливает заголовок, который содержит более 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 создать базу данных, а также хотите изменить имя создаваемых таблиц. Тогда —

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

  • Атрибут таблицы переопределяет это соглашение по умолчанию.

  • EF Code First создаст таблицу с указанным именем в атрибуте Table для данного класса домена.

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

Атрибут таблицы переопределяет это соглашение по умолчанию.

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

Давайте посмотрим на следующий пример, в котором класс называется «Студент», и по соглашению Code First предполагает, что он будет сопоставлен с таблицей «Студенты». Если это не так, вы можете указать имя таблицы с атрибутом 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 в схеме администратора, как показано на следующем рисунке.

Схема администратора

колонка

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

  • Атрибут столбца переопределяет соглашение по умолчанию.

  • EF Code First создаст столбец с указанным именем в атрибуте Column для данного свойства.

Атрибут столбца переопределяет соглашение по умолчанию.

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_ <имя свойства>

Вы можете создать индекс для одного или нескольких столбцов, используя IndexAttribute.

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

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

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

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

По умолчанию индекс будет иметь имя IX_ <имя свойства>

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

public class Course {
   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 Кредиты

По умолчанию индексы не являются уникальными, но вы можете использовать именованный параметр 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, но свойство StdntID относится к классу Student.

Решением для этого является создание свойства навигации в 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.

Атрибут NotMapped

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

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 создает столбец внешнего ключа {Имя класса} _ {Первичный ключ}, если свойство внешнего ключа не включено в определенный класс, как показано в приведенных выше классах. Когда база данных будет сгенерирована, вы увидите следующие внешние ключи.

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

Как вы можете видеть, сначала Code не может сопоставить свойства двух классов самостоятельно. Таблица базы данных для заявок должна иметь один внешний ключ для 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, как показано на следующем рисунке.

Столбцы внешнего ключа

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