Типы сущностей с конструкторами
Можно определить конструктор с параметрами и вызвать этот конструктор EF Core при создании экземпляра сущности. Параметры конструктора можно привязать к сопоставленным свойствам или различным типам служб, чтобы упростить поведение, например отложенную загрузку.
Примечание
В настоящее время все привязки конструктора являются по соглашению. Конфигурация конкретных конструкторов для использования планируется для будущего выпуска.
Рассмотрим типичную модель блога или записи:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Когда EF Core создает экземпляры этих типов, например для результатов запроса, сначала вызовет конструктор без параметров по умолчанию, а затем присвоит каждому свойству значение из базы данных. Однако если EF Core находит параметризованный конструктор с именами параметров и типами, которые соответствуют сопоставленным свойствам, вместо этого вызовет параметризованный конструктор со значениями для этих свойств и не будет явно задавать каждое свойство. Например:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Обратите внимание на следующие моменты.
- Не все свойства должны иметь параметры конструктора. Например, свойство Post.Content не задано никаким параметром конструктора, поэтому EF Core установит его после вызова конструктора обычным образом.
- Типы параметров и имена должны соответствовать типам свойств и именам, за исключением того, что свойства можно регистрировать, пока параметры регистрируются верблюдю.
- EF Core не может задать свойства навигации (например, блог или записи выше) с помощью конструктора.
- Конструктор может быть общедоступным, частным или иметь другие специальные возможности. Тем не менее, неактивные прокси-серверы требуют, чтобы конструктор был доступен из наследующего класса прокси. Обычно это означает, что делает его общедоступным или защищенным.
После задания свойств с помощью конструктора может быть смысл сделать некоторые из них доступны только для чтения. EF Core поддерживает это, но есть некоторые вещи, которые следует искать:
- Свойства без наборов не сопоставляются с соглашением. (Это, как правило, сопоставляет свойства, которые не должны быть сопоставлены, например вычисляемые свойства.)
- Для использования автоматически созданных значений ключей требуется свойство ключа, которое является чтением и записью, так как значение ключа необходимо задать генератором ключей при вставке новых сущностей.
Простой способ избежать этих вещей заключается в использовании частных наборов. Например:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Author { get; private set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; private set; }
public string Title { get; private set; }
public string Content { get; set; }
public DateTime PostedOn { get; private set; }
public Blog Blog { get; set; }
}
EF Core видит свойство с частным набором как чтение и запись, что означает, что все свойства сопоставляются как раньше, а ключ по-прежнему может быть создан в хранилище.
Альтернативой использованию частных наборов является создание свойств только для чтения и добавление более явного сопоставления в OnModelCreating. Аналогичным образом некоторые свойства можно удалить полностью и заменить только полями. Например, рассмотрим такие типы сущностей:
public class Blog
{
private int _id;
public Blog(string name, string author)
{
Name = name;
Author = author;
}
public string Name { get; }
public string Author { get; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
private int _id;
public Post(string title, DateTime postedOn)
{
Title = title;
PostedOn = postedOn;
}
public string Title { get; }
public string Content { get; set; }
public DateTime PostedOn { get; }
public Blog Blog { get; set; }
}
И эта конфигурация в OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Author);
b.Property(e => e.Name);
});
modelBuilder.Entity<Post>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Title);
b.Property(e => e.PostedOn);
});
}
Следует учесть:
- Ключ "property" теперь является полем. Это не
readonly
поле, чтобы можно было использовать ключи, созданные в магазине. - Другие свойства — это свойства только для чтения, заданные только в конструкторе.
- Если значение первичного ключа устанавливается только EF или считывается из базы данных, то его не нужно включать в конструктор. Это оставляет ключ "свойство" простым полем и дает понять, что он не должен быть явно задан при создании новых блогов или записей.
Примечание
Этот код приведет к предупреждению компилятора "169", указывающее, что поле никогда не используется. Это можно игнорировать, так как в действительности EF Core использует поле экстралингвистическим образом.
EF Core также может внедрять "службы" в конструктор типа сущности. Например, можно внедрить следующие элементы:
DbContext
— текущий экземпляр контекста, который также можно ввести в качестве производного типа DbContext.ILazyLoader
— служба отложенной загрузки см . в документации по отложенной загрузке для получения дополнительных сведенийAction<object, string>
— делегат отложенной загрузки- см . документацию по отложенной загрузке для получения дополнительных сведенийIEntityType
— метаданные EF Core, связанные с этим типом сущности
Примечание
В настоящее время можно внедрить только службы, известные EF Core. Поддержка внедрения служб приложений рассматривается в будущем выпуске.
Например, внедренный DbContext можно использовать для выборочного доступа к базе данных для получения сведений о связанных сущностях без их загрузки. В приведенном ниже примере используется для получения количества записей в блоге без загрузки записей:
public class Blog
{
public Blog()
{
}
private Blog(BloggingContext context)
{
Context = context;
}
private BloggingContext Context { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; set; }
public int PostsCount
=> Posts?.Count
?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
?? 0;
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Некоторые вещи, которые следует заметить об этом:
- Конструктор является частным, так как он всегда вызывается EF Core, и существует другой общедоступный конструктор для общего использования.
- Код, использующий внедренную службу (т. е. контекст), защищает его от того,
null
чтобы обрабатывать случаи, когда EF Core не создает экземпляр. - Так как служба хранится в свойстве чтения и записи, она сбрасывается при присоединении сущности к новому экземпляру контекста.
Предупреждение
Внедрение DbContext, как это, часто считается анти-шаблоном, так как он связывает типы сущностей непосредственно с EF Core. Тщательно рассмотрите все варианты перед использованием внедрения служб, как это.
Отзыв о .NET
.NET — это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв: