Поделиться через


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

Включение 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 fluent:

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");
            });
    }
}