По умолчанию все типы сущностей, для которых определены в контексте данных наборы DbSet, включаются в модель и в дальнейшем сопоставляются с таблицами в базе данных. Например:
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } } public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } }
В данном случае, поскольку для класса User в классе контекста определено свойство типа DbSet
public DbSet<User> Users { get; set; } = null!;
Таким образом, для сущности User будет создана таблица в бд.
Класс DbSet, как и другие типы, является ссылочным. А, начиная с C# 10 и .NET 6 автоматически применяется функциональность ссылочных nullable-типов. И переменные/свойства тех типов, которые не являются nullable, следует инициализировать некотором значением перед их использованием. Например, если мы напишем:
public DbSet<User> Users { get; set; }
То мы столкнемся с предупреждением:
То есть нам надо инициализировать свойство типа DbSet. Хотя в этом нет большого смысла, так как контструктор базового класса DbContext гарантирует, что все свойства типа DbSet будут инициализированы и соответственно в принципе не будут иметь значение null.
Тем не менее проблема остается, поскольку мы сталкиваемся с предупреждением. Чтобы выйти из этой ситуации мы можем инициализировать свойство с помощью выражения null!, которое говорит, что данное свойство в принципе не будет иметь значение null:
public DbSet<User> Users { get; set; } = null!;
Другое решение - инициализировать свойство значением типа Set<T>:
public DbSet<User> Users => Set<User>();
Но кроме того, в модель также включаются типы, на которые есть ссылки в сущностях, которые уже включены в модель, например, через свойства DbSet.
Например, пусть у нас определены следующие сущности:
public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } // навигационное свойство public Company? Company { get; set; } } public class Company { public int Id { get; set; } public string? Name { get; set; } } public class Country { public int Id { get; set; } public string? Name { get; set; } }
И пусть у нас будет класс контекста данных:
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } }
После создания базы данных в ней будут созданы две таблицы: Users и Company. А третий класс - Country никак не используется в сущностях User и Company, для Country нет свойства DbSet в классе контекста, поэтому она не будет включена в контекст и для нее не будет создаваться таблица в бд.
Поскольку для типа User определен набор DbSet, то для имени таблицы будет применяться имя этого набора, а для второй таблицы будет использоваться имя класса Company.
Еще один способ включения сущности в модель представляет вызов Entity() объекта ModelBuilder в методе OnModelCreating():
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Country>(); } }
И если мы сейчас создадим и выполним миграции, то в базе данных будут уже три таблицы для сущностей:
Иногда возникают ситуации, когда надо, наоборот, исключить сущность из модели. Например, в примере выше сущность Company ссылается на класс Company, и, допустим, мы не хотим, чтобы в базе данных была таблица Company. В этом случае мы можем использовать Fluent API или аннотации данных.
Применение Fluent API заключается в вызове метода Ignore():
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Ignore<Company>(); } }
Аннотации данных предполагают установку над классом атрибута [NotMapped]:
using System.ComponentModel.DataAnnotations.Schema; public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } // навигационное свойство public Company? Company { get; set; } } [NotMapped] public class Company { public int Id { get; set; } public string Name { get; set; } }
При исключении сущности Company в базе данных будет только одна таблица Users, причем она не будет содержать столбца, который бы сопоставлялся со свойством Company класса User: