Учебники

Entity Framework — первый пример

Давайте определим очень простую модель с использованием классов. Мы просто определяем их в файле Program.cs, но в реальном приложении вы разделите ваши классы на отдельные файлы и, возможно, на отдельный проект. Ниже приведена модель данных, которую мы будем создавать с использованием подхода Code First.

Модель с использованием классов

Создать модель

Добавьте следующие три класса в файл Program.cs, используя следующий код для класса Student.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}
  • Свойство ID станет столбцом первичного ключа таблицы базы данных, соответствующей этому классу.

  • Свойство Enrollments является свойством навигации. Свойства навигации содержат другие объекты, связанные с этим объектом.

  • В этом случае свойство Enrollments сущности Student будет содержать все сущности Enrollment, связанные с этой сущностью Student.

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

  • Если свойство навигации может содержать несколько объектов (как в отношениях «многие ко многим» или «один к одному»), его тип должен быть списком, в который можно добавлять, удалять и обновлять записи, например ICollection.

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

Свойство Enrollments является свойством навигации. Свойства навигации содержат другие объекты, связанные с этим объектом.

В этом случае свойство Enrollments сущности Student будет содержать все сущности Enrollment, связанные с этой сущностью Student.

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

Если свойство навигации может содержать несколько объектов (как в отношениях «многие ко многим» или «один к одному»), его тип должен быть списком, в который можно добавлять, удалять и обновлять записи, например ICollection.

Ниже приведена реализация для курса.

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Свойство Enrollments является свойством навигации. Сущность курса может быть связана с любым количеством сущностей зачисления.

Ниже приведена реализация класса Enumement и enum.

public enum Grade {
   A, B, C, D, F
}

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; }
}
  • Свойство EnrollmentID будет первичным ключом.

  • Свойство Grade является перечислением. Знак вопроса после объявления типа Grade указывает, что свойство Grade имеет значение NULL.

  • Нулевая оценка отличается от нулевой оценки. Нуль означает, что оценка не известна или еще не была назначена.

  • Свойства StudentID и CourseID являются внешними ключами, а соответствующие свойства навигации — Student и Course.

  • Объект Enrollment связан с одним объектом Student и одним объектом Course, поэтому свойство может содержать только один объект Student и Course.

Свойство EnrollmentID будет первичным ключом.

Свойство Grade является перечислением. Знак вопроса после объявления типа Grade указывает, что свойство Grade имеет значение NULL.

Нулевая оценка отличается от нулевой оценки. Нуль означает, что оценка не известна или еще не была назначена.

Свойства StudentID и CourseID являются внешними ключами, а соответствующие свойства навигации — Student и Course.

Объект Enrollment связан с одним объектом Student и одним объектом Course, поэтому свойство может содержать только один объект Student и Course.

Создать базу данных контекста

Основным классом, который координирует функциональность Entity Framework для данной модели данных, является класс контекста базы данных, который позволяет запрашивать и сохранять данные. Вы можете создать этот класс, наследуя от класса DbContext и выставляя типизированный DbSet для каждого класса в нашей модели. Ниже приведена реализация класса MyContext, который является производным от класса DbContext.

public class MyContext : DbContext {
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Ниже приведен полный код в файле Program.cs.

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

   public enum Grade {
      A, B, C, D, F
   }

   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 {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Приведенный выше код — это все, что нам нужно, чтобы начать хранить и извлекать данные. Давайте добавим некоторые данные и затем получим их. Ниже приведен код в методе main.

static void Main(string[] args) {

   using (var context = new MyContext()) {
      // Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);
		
      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

      // Display all Students from the database
      var students = (from s in context.Students 
         orderby s.FirstMidName select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }
		
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

Когда приведенный выше код будет выполнен, вы получите следующий вывод.

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

Теперь возникает вопрос, где находятся данные и база данных, в которую мы добавили некоторые данные, а затем извлекли их из базы данных. По соглашению, DbContext создал базу данных для вас.

  • Если доступен локальный экземпляр SQL Express, Code First создаст базу данных для этого экземпляра.

  • Если SQL Express недоступен, Code First попытается использовать LocalDb.

  • База данных названа в честь полного имени производного контекста.

Если доступен локальный экземпляр SQL Express, Code First создаст базу данных для этого экземпляра.

Если SQL Express недоступен, Code First попытается использовать LocalDb.

База данных названа в честь полного имени производного контекста.

В нашем случае экземпляр SQL Express доступен, а имя базы данных — EFCodeFirstDemo.MyContext, как показано на следующем рисунке.

Экземпляр SQL Express

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

  • Как вы можете видеть на изображении выше, он создал таблицы «Студенты», «Курсы» и «Зачисления», и каждая таблица содержит столбцы с соответствующим типом данных и длиной.

  • Имена столбцов и тип данных также соответствуют свойствам соответствующих классов домена.

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

Как вы можете видеть на изображении выше, он создал таблицы «Студенты», «Курсы» и «Зачисления», и каждая таблица содержит столбцы с соответствующим типом данных и длиной.

Имена столбцов и тип данных также соответствуют свойствам соответствующих классов домена.

Инициализация базы данных

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

Инициализация базы данных

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

  • Нет параметра
  • Имя базы данных
  • Имя строки подключения

Нет параметра

Если вы укажете базовый конструктор класса контекста без каких-либо параметров, как показано в приведенном выше примере, тогда структура сущностей создаст базу данных на локальном сервере SQLEXPRESS с именем {Namespace}. {Имя класса контекста}.

В приведенном выше примере база данных, которая создается автоматически, имеет имя EFCodeFirstDemo.MyContext. Если вы посмотрите на имя, вы обнаружите, что EFCodeFirstDemo является пространством имен, а MyContext является именем класса контекста, как показано в следующем коде.

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Имя базы данных

Если вы передадите имя базы данных в качестве параметра в базовом конструкторе класса контекста, то Code First снова автоматически создаст базу данных, но на этот раз это будет имя, переданное в качестве параметра в базовом конструкторе на локальном сервере базы данных SQLEXPRESS. ,

В следующем коде MyContextDB указывается в качестве параметра в базовом конструкторе. Если запустить ваше приложение, то база данных с именем MyContextDB будет создана на вашем локальном сервере SQL.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Имя строки подключения

Это простой способ указать DbContext использовать сервер базы данных, отличный от SQL Express или LocalDb. Вы можете поместить строку подключения в файл app.config.

  • Если имя строки соединения совпадает с именем вашего контекста (либо с квалификацией пространства имен, либо без нее), то она будет найдена DbContext при использовании конструктора без параметра.

  • Если имя строки подключения отличается от имени вашего контекста, вы можете указать DbContext использовать это подключение в режиме Code First, передав имя строки подключения конструктору DbContext.

Если имя строки соединения совпадает с именем вашего контекста (либо с квалификацией пространства имен, либо без нее), то она будет найдена DbContext при использовании конструктора без параметра.

Если имя строки подключения отличается от имени вашего контекста, вы можете указать DbContext использовать это подключение в режиме Code First, передав имя строки подключения конструктору DbContext.

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}
  • В приведенном выше коде фрагмент строки подключения класса контекста указан в качестве параметра в базовом конструкторе.

  • Имя строки подключения должно начинаться с «name =», в противном случае оно будет считаться именем базы данных.

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

В приведенном выше коде фрагмент строки подключения класса контекста указан в качестве параметра в базовом конструкторе.

Имя строки подключения должно начинаться с «name =», в противном случае оно будет считаться именем базы данных.

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

<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • Имя базы данных в строке подключения в app.config — EFMyContextDB . CodeFirst создаст новую базу данных EFMyContextDB или использует существующую базу данных EFMyContextDB на локальном сервере SQL.

Имя базы данных в строке подключения в app.config — EFMyContextDB . CodeFirst создаст новую базу данных EFMyContextDB или использует существующую базу данных EFMyContextDB на локальном сервере SQL.

Доменные классы

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

  • Аннотации данных
  • Свободный API

Аннотации данных

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

Ниже приведены аннотации данных, используемые в классе ученика.

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

Свободный API

Большая часть конфигурации модели может быть выполнена с использованием простых аннотаций данных. Свободный API — это расширенный способ задания конфигурации модели, который охватывает все, что могут делать аннотации данных, в дополнение к некоторым более сложным конфигурациям, невозможным для аннотаций данных. Аннотации данных и свободный API могут использоваться вместе.

Для доступа к свободному API вы переопределяете метод OnModelCreating в DbContext. Теперь давайте переименуем имя столбца в таблице ученика из FirstMidName в FirstName, как показано в следующем коде.