Типы сущностей

Включение dbSet типа в контекст означает, что он включен в модель EF Core; мы обычно называем такой тип сущностью. EF Core может считывать и записывать экземпляры сущностей из базы данных и в нее, а если вы используете реляционную базу данных, EF Core может создавать таблицы для ваших сущностей с помощью миграции.

Включение типов в модель

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

В приведенном ниже примере кода включены все типы:

  • Blog включается, так как он предоставляется в свойстве DbSet в контексте.
  • Post включается, так как обнаруживается с помощью свойства навигации Blog.Posts .
  • AuditEntry так как он указан в OnModelCreating.
internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AuditEntry>();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

public class AuditEntry
{
    public int AuditEntryId { get; set; }
    public string Username { get; set; }
    public string Action { get; set; }
}

Исключение типов из модели

Если вы не хотите, чтобы тип включался в модель, его можно исключить:

[NotMapped]
public class BlogMetadata
{
    public DateTime LoadedFromDatabase { get; set; }
}

Исключение из миграций

Иногда полезно иметь один и тот же тип сущности, сопоставленный в нескольких типах DbContext . Это особенно актуально при использовании ограниченных контекстов, для которых для каждого ограниченного контекста обычно используется отдельный DbContext тип.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}

При использовании этой конфигурации миграция не создает таблицу AspNetUsers , но IdentityUser по-прежнему включается в модель и может использоваться в обычном режиме.

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

Имя таблицы

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

Имя таблицы можно настроить вручную:

[Table("blogs")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Схема таблицы

При использовании реляционной базы данных таблицы создаются по соглашению в схеме базы данных по умолчанию. Например, microsoft SQL Server будет использовать схему dbo (SQLite не поддерживает схемы).

Вы можете настроить таблицы для создания в определенной схеме следующим образом:

[Table("blogs", Schema = "blogging")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Вместо того чтобы указывать схему для каждой таблицы, можно также определить схему по умолчанию на уровне модели с помощью текучих API:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDefaultSchema("blogging");
}

Обратите внимание, что установка схемы по умолчанию также повлияет на другие объекты базы данных, такие как последовательности.

Сопоставление представлений

Типы сущностей можно сопоставить с представлениями базы данных с помощью API Fluent.

Примечание

EF предполагает, что указанное представление уже существует в базе данных, и не создаст его автоматически при миграции.

modelBuilder.Entity<Blog>()
    .ToView("blogsView", schema: "blogging");

Сопоставление с представлением приведет к удалению сопоставления таблицы по умолчанию, но тип сущности также может быть сопоставлен с таблицей явным образом. В этом случае сопоставление запросов будет использоваться для запросов, а сопоставление таблиц — для обновлений.

Совет

Чтобы протестировать типы сущностей без ключей, сопоставленные с представлениями с помощью поставщика в памяти, сопоставьте их с запросом с помощью ToInMemoryQuery. Дополнительные сведения см. в документации по поставщику в памяти .

Сопоставление функций с табличным значением

Можно сопоставить тип сущности с функцией с табличным значением (TVF), а не с таблицей в базе данных. Чтобы проиллюстрировать это, давайте определим другую сущность, представляющую блог с несколькими записями. В этом примере сущность является бессерверной, но она не обязательно должна быть.

public class BlogWithMultiplePosts
{
    public string Url { get; set; }
    public int PostCount { get; set; }
}

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

CREATE FUNCTION dbo.BlogsWithMultiplePosts()
RETURNS TABLE
AS
RETURN
(
    SELECT b.Url, COUNT(p.BlogId) AS PostCount
    FROM Blogs AS b
    JOIN Posts AS p ON b.BlogId = p.BlogId
    GROUP BY b.BlogId, b.Url
    HAVING COUNT(p.BlogId) > 1
)

Теперь сущность BlogWithMultiplePosts можно сопоставить с этой функцией следующим образом:

modelBuilder.Entity<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");

Примечание

Чтобы сопоставить сущность с функцией с табличным значением, функция должна быть без параметров.

Обычно свойства сущности сопоставляются с соответствующими столбцами, возвращаемыми TVF. Если имена столбцов, возвращаемых TVF, отличаются от имен свойства сущности, то столбцы сущности можно настроить с помощью HasColumnName метода, как и при сопоставлении с обычной таблицей.

При сопоставлении типа сущности с возвращающей табличное значение функцией запрос:

var query = from b in context.Set<BlogWithMultiplePosts>()
            where b.PostCount > 3
            select new { b.Url, b.PostCount };

Преобразуется в следующий запрос SQL:

SELECT [b].[Url], [b].[PostCount]
FROM [dbo].[BlogsWithMultiplePosts]() AS [b]
WHERE [b].[PostCount] > 3

Примечания к таблице

Вы можете задать произвольный текстовый комментарий, который будет задан в таблице базы данных, что позволяет документировать схему в базе данных:

[Comment("Blogs managed on the website")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Типы сущностей общего типа

Типы сущностей, использующие один и тот же тип CLR, называются типами сущностей общего типа. Эти типы сущностей должны быть настроены с уникальным именем, которое должно быть указано при каждом использовании типа сущности общего типа в дополнение к типу CLR. Это означает, что соответствующее DbSet свойство должно быть реализовано Set с помощью вызова .

internal class MyContext : DbContext
{
    public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
            "Blog", bb =>
            {
                bb.Property<int>("BlogId");
                bb.Property<string>("Url");
                bb.Property<DateTime>("LastUpdated");
            });
    }
}