Когда EF Core создает объект сущности, например, при после получения данных из БД, он вначале вызывае конструктор по умолчанию, который не имеет параметров, и затем передает каждому свойству полученные из бд значения.
Если EF Core находит конструктор с параметрами, где названия и типы параметров соответствуют устанавливаемым свойствам,
то вместо установки свойств EF передает полученные из БД значения параметрам конструктора. При этом между параметрами и свойствами должно быть соответствие по
типу и имени за тем исключением, что названия могут отличаться по регистру, например, свойство Name
и параметр name
.
Рассмотрим на примере. Допустим, у нас есть следующая сущность User:
public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public User(string name, int age) { Name = name; Age = age; Console.WriteLine($"Вызов конструктора для объекта {name}"); } }
Класс User имеет три свойства и через конструктор устанавливает два из них.
Пусть у нас будет стандартный контекст данных:
public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } }
И, допустим, в программе создаем несколько объектов User, добавляем их в БД и получаем обратно из БД:
using (ApplicationContext db = new ApplicationContext()) { db.Database.EnsureDeleted(); db.Database.EnsureCreated(); User tom = new User("Tom", 37); User bob = new User("Bob", 41); db.Users.Add(tom); db.Users.Add(bob); db.SaveChanges(); } using (ApplicationContext db = new ApplicationContext()) { Console.WriteLine("Получение данных из БД"); var users = db.Users.ToList(); foreach(var user in users) Console.WriteLine($"{user.Name} - {user.Age}"); }
Здесь при получении данных при выполнении метода db.Users.ToList()
EF Core будет вызывать для каждой полученной строки из таблицы
объект User, вызывая его конструктор с двумя параметрами. Для наглядности в примере выше разделы операции добавления и получения по разным объектам контекста.
В итоге мы получим следующий консольный вывод:
Вызов конструктора для объекта Tom Вызов конструктора для объекта Bob Получение данных из БД Вызов конструктора для объекта Tom Вызов конструктора для объекта Bob Tom - 37 Bob - 41
Здесь надо учитывать несколько моментов:
Необязательно для всех свойств определять в конструкторе свои параметры. Например, свойство Id не устанавливается в конструкторе. Те свойства, для которых в конструкторе не определено параметров, устанавливаются напрямую, как в общем случае.
Параметры и свойства должны соответствовать по имени и типу за исключением регистра имени.
Конструкторы могут иметь любой модификатор доступа, в том числе, private
.
EF Core НЕ устанавливает таким обазом навигационные свойства, которые представляют другие сущности и имеют конструктор:
public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public Company? Company { get; set; } public User(string name, int age) { Name = name; Age = age; } } public class Company { public int Id { get; set; } public string Name { get; set; } public ICollection<User> Users { get; set; } = new List<User>(); public Company(string name) => Name = name; }
При этом класс может определять несколько конструкторов с разным количеством параметров:
public class User { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public User(string name) { Name = name; Age = 18; } public User(string name, int age) { Name = name; Age = age; } }