Typy jednostek

Uwzględnienie elementu DbSet typu w kontekście oznacza, że jest on uwzględniony w modelu platformy EF Core; zazwyczaj nazywamy taki typ jak jednostka. Program EF Core może odczytywać i zapisywać wystąpienia jednostek z/do bazy danych, a jeśli używasz relacyjnej bazy danych, program EF Core może tworzyć tabele dla jednostek za pośrednictwem migracji.

Dołączanie typów w modelu

Zgodnie z konwencją typy uwidocznione we właściwościach dbSet w kontekście są uwzględniane w modelu jako jednostki. Typy jednostek określone w metodzie OnModelCreating są również uwzględniane, podobnie jak wszelkie typy, które są odnajdywane rekursywnie eksplorując właściwości nawigacji innych odnalezionych typów jednostek.

W poniższym przykładzie kodu uwzględniono wszystkie typy:

  • Blog element jest uwzględniony, ponieważ jest uwidoczniony we właściwości DbSet w kontekście.
  • Post element jest uwzględniony, ponieważ został odnaleziony za pośrednictwem Blog.Posts właściwości nawigacji.
  • AuditEntry ponieważ jest określony w pliku 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; }
}

Wykluczanie typów z modelu

Jeśli nie chcesz, aby typ został uwzględniony w modelu, możesz go wykluczyć:

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

Wykluczanie z migracji

Czasami przydatne jest mapowanie tego samego typu jednostki w wielu DbContext typach. Jest to szczególnie istotne w przypadku używania ograniczonych kontekstów, dla których często występuje inny DbContext typ dla każdego ograniczonego kontekstu.

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

W przypadku tych migracji konfiguracji nie zostanie utworzona AspNetUsers tabela, ale IdentityUser nadal jest uwzględniona w modelu i może być używana normalnie.

Jeśli musisz ponownie rozpocząć zarządzanie tabelą przy użyciu migracji, należy utworzyć nową migrację, w której AspNetUsers nie zostanie wykluczona. Następna migracja będzie teraz zawierać wszelkie zmiany wprowadzone w tabeli.

Nazwa tabeli

Zgodnie z konwencją każdy typ jednostki zostanie skonfigurowany do mapowania na tabelę bazy danych o takiej samej nazwie jak właściwość DbSet, która uwidacznia jednostkę. Jeśli dla danej jednostki nie istnieje zestaw DbSet, używana jest nazwa klasy.

Możesz ręcznie skonfigurować nazwę tabeli:

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

Schemat tabeli

W przypadku korzystania z relacyjnej bazy danych tabele są tworzone zgodnie z konwencją w domyślnym schemacie bazy danych. Na przykład program Microsoft SQL Server będzie używać schematu dbo (SQLite nie obsługuje schematów).

Tabele można skonfigurować do utworzenia w określonym schemacie w następujący sposób:

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

Zamiast określać schemat dla każdej tabeli, można również zdefiniować domyślny schemat na poziomie modelu za pomocą płynnego interfejsu API:

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

Należy pamiętać, że ustawienie domyślnego schematu będzie również miało wpływ na inne obiekty bazy danych, takie jak sekwencje.

Wyświetlanie mapowania

Typy jednostek można mapować na widoki bazy danych przy użyciu interfejsu API Fluent.

Uwaga

Program EF zakłada, że widok, do którego odwołuje się odwołanie, już istnieje w bazie danych, nie utworzy go automatycznie w ramach migracji.

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

Mapowanie na widok spowoduje usunięcie domyślnego mapowania tabeli, ale typ jednostki można również zamapować do tabeli jawnie. W takim przypadku mapowanie zapytań będzie używane dla zapytań, a mapowanie tabeli będzie używane do aktualizacji.

Napiwek

Aby przetestować typy jednostek bez klucza mapowane na widoki przy użyciu dostawcy w pamięci, zamapuj je na zapytanie za pomocą metody ToInMemoryQuery. Aby uzyskać więcej informacji, zobacz dokumentację dostawcy w pamięci.

Mapowanie funkcji wartości tabeli

Można mapować typ jednostki na funkcję o wartości tabeli (TVF) zamiast tabeli w bazie danych. Aby to zilustrować, zdefiniujmy inną jednostkę reprezentującą blog z wieloma wpisami. W tym przykładzie jednostka jest bez klucza, ale nie musi być.

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

Następnie utwórz następującą funkcję o wartości tabeli w bazie danych, która zwraca tylko blogi z wieloma wpisami, a także liczbę wpisów skojarzonych z każdym z tych blogów:

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
)

Teraz jednostkę BlogWithMultiplePosts można zamapować na tę funkcję w następujący sposób:

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

Uwaga

Aby zamapować jednostkę na funkcję wartości tabeli, funkcja musi być bez parametrów.

Konwencjonalnie właściwości jednostki zostaną zamapowane na pasujące kolumny zwrócone przez tvf. Jeśli kolumny zwracane przez program TVF mają inne nazwy niż właściwość jednostki, kolumny jednostki można skonfigurować przy użyciu HasColumnName metody, podobnie jak podczas mapowania na zwykłą tabelę.

Gdy typ jednostki jest mapowany na funkcję wartości tabeli, zapytanie:

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

Tworzy następujący kod SQL:

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

Komentarze do tabeli

Możesz ustawić dowolny komentarz tekstowy ustawiony w tabeli bazy danych, co umożliwia dokumentowanie schematu w bazie danych:

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

Typy jednostek typu współużytkowanego

Typy jednostek używające tego samego typu CLR są nazywane typami jednostek typu współużytkowanego. Te typy jednostek należy skonfigurować przy użyciu unikatowej nazwy, która musi być podana za każdym razem, gdy używany jest typ jednostki typu współużytkowanego, oprócz typu CLR. Oznacza to, że odpowiednią DbSet właściwość należy zaimplementować przy użyciu wywołania 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");
            });
    }
}