Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Можно определить конструктор с параметрами и заставить 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 установит его после вызова конструктора обычным образом.
- Типы и имена параметров должны соответствовать типам и именам свойств, за исключением того, что свойства могут быть записаны с использованием стиля "PascalCase", тогда как параметры - в стиле "camelCase".
- 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. Тщательно рассмотрите все варианты перед использованием внедрения служб, как это.