Porovnání funkcí Table Splitting a Owned Entity Types v EF Core
Článek se vztahuje k verzi produktu EF Core 2.0
Tento článek byl napsán v roce 2018. Vývojářské technologie se neustále inovují a článek již nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.
Jednou z nových funkcí, kterou nabízí EF Core je tzv. Table Splitting. Do jisté míry tato funkce vypadá podobně jako komplexní typy (tzv. Owned Entity Types), ale v některých detailech se liší. V článku porovnám obě funkce a nastíním rozdíly mezi nimi.
Table Splitting
Z hlediska doménového modelu se dá říci, že se jedná o mapování typů 1:1 s tím, že obě doménové třídy se mapují na jednu společnou DB tabulku. Obě entity pak následně sdílí společný primární klíč. Výhodou promapování je především projekce, při které lze pracovat jen s hlavní množinou dat, přičemž druhá část vazby se donačítá až v případě potřeby.
public class Category { public int CategoryId {get; set;} public string Name {get; set;} public CategoryDetail CategoryDetail {get; set;} } public class CategoryDetail { public int CategoryId {get; set;} public DateTime Created {get; set;} }
Konfigurace
Konfigurace vychází ze vztahu 1:1 a liší se pouze tím, že obě konfigurované entity mají definovaný stejný název DB tabulky, do které se mapují. Konfigurace je možná jak pomocí Fluent API (doporučuji), tak pomocí Data Annotations.
// pro Order.cs builder.ToTable("Categories"); builder.HasOne(x => x.CategoryDetail) .WithOne(x => x.Category) .HasForeignKey<CategoryDetail>(x => x.CategoryId); // pro OrderDetail.cs builder.ToTable("Categories"); builder.HasOne(x => x.Category) .WithOne(x => x.CategoryDetail) .HasForeignKey<CategoryDetail>(x => x.CategoryId);
Performance
Výhoda Table splittingu spočívá v tom, že veškerá data vztahu 1:1 jsou v jedné DB tabulce. Díky tomu se nemusí provádět JOIN v případě, kdy chce uživatel načíst všechna data. V případě, že uživatel potřebuje jen řídící entitu, proběhne projekce s načtením jen nezbytných sloupců relevantních dané CS třídě.
var categories = context.Categories.ToList(); SELECT [c].[CategoryId], [c].[Name] FROM [dbo].[Categories] AS [c] var categoriesWithDetails = context.Categories.Include(x => x.CategoryDetail).ToList(); SELECT [x].[CategoryId], [x].[Name], [x].[Created] FROM [dbo].[Categories] AS [x]
Owned Entity Types
V porovnání s Table Splitingem jsou Owned Entity Types v některých drobnostech odlišné. Owned Entity Types jsou v doménovém modelu reprezentovány CS třídou (tzv. komplexní typ), která je k řídící entitě vázána jako property bez jakéhokoliv klíče. Oproti Table Splittingu tedy komplexní typ nemá primární klíč a ve své podstatě jen sdružuje některé vlastnosti do třídy. Taková třída se v rámci řídíci entity často používá opakovaně. Typickým užitím jsou komplexní třídy Name, Address nebo Price.
public class Order { public int OrderId {get; set;} public Address BillingAddress {get; set;} public Address ShippingAddress {get; set;} } public class Address { public string FirstName {get; set;} public string LastName {get; set;} public string Email {get; set;} }
Konfigurace
Pro řídící entitu se nastavuje vlastnosti OwnsOne proti vybrané property, která drží daný komplexní typ. Příjemné je, že pro každý Owned Type lze nastavit dodatečná pravidla mapování a změnit tak název výsledného sloupce, datový typ nebo například úplnou ignoraci sloupce (viz př. níže).
// pro Order.cs builder.OwnsOne(x => x.BillingAddress); builder.OwnsOne(x => x.ShippingAddress, sa => { sa.Ignore(x => x.Email); });
Performance
Podobně jako v předchozím případě probíhá pouze projekce nad jednou DB tabulkou. Oproti Table Splittingu však probíhá vždy kompletní načtení všech Owned Types.
var order = context.Orders.ToList(); = SELECT [o].[OrderId], [o].[ShippingAddress_FirstName], [o].[ShippingAddress_LastName], [o].[BillingAddress_Email], [o].[BillingAddress_FirstName], [o].[BillingAddress_LastName] FROM [dbo].[Orders] AS [o]
Závěr
Obě funkce najdou uplatnění v mnoha modelech a mohou vývojáři usnadnit život. Ani jedna z funkcí nepředstavuje rizika z hlediska výkonnosti a přitom nabízí možnost pohodlnější práce s doménovým modelem. Owned Entity Types jsou ideální pro opakující se komplexní typy jako je například cena, nad kterou lze aplikovat další užitečné modely (dopočtení ceny s DPH, bez DPH atd.).