Z mnoha důvodů, namátkou: 1) Entity obsahují i sloupce, které se týkají toho, jak jsou data reprezentována v databázi (např. cizí klíče atd.) - nemám rád, když implementační detaily týkající se relačních databází prosakují výše než do business vrstvy - nemají tam co dělat. Správně by se při návrhu aplikace nemělo začínat návrhem schématu databáze, ale měl by se udělat doménový model. Pak by se teprve mělo přemýšlet, jak bude vypadat storage model a jak se na sebe budou mapovat. Praxe je samozřejmě jiná, nicméně horní vrstvy aplikace by neměly řešit, jak jsou data persistována, to je interní věc. 2) V UI málokdy zobrazuji přesně jen data z jedné tabulky, ale většinou v dotazech potřebuju JOIN. Ano, můžu udělat context.Products.Include(p => p.Category).Include(p => p.Supplier).Include(p => p.Orders) a do UI dát celé entity, ale v gridu z tabulky Categories a Suppliers potřebuju jen sloupec Name a ne těch 10 dalších, a na vlastnosti Orders zavolám jen .Count - je tedy zbytečné vytahovat všechno, když potřebuju jen něco. Takže si nadeklaruju třídu, která bude vypadat asi takto:
public class ProductDTO {
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public string CategoryName { get; set; }
public string SupplierName { get; set; }
public int OrdersCount { get; set; }
}
Na mapování používám knihovnu AutoMapper, které řeknu, ať mi třídu Product namapuje na ProductDTO. Ta knihovna je inteligentní, takže když vidí, že Product nemá vlastnost CategoryName, ale že jde zavolat Category.Name, tak to použije, stejně tak když vlastnost končí Count a to předtím je kolekce, a jde volat Orders.Count(), tak to udělá. A dokonce umí vygenerovat lambda expression nad IQueryable, takže to použiju přímo v dotazu a přeloží se to celé do SQL:
context.Products.Project().To<ProductDTO>()
Dá se pochopitelně použít i pro mapování opačným směrem, takže pokud chci editovat nějaký řádek tabulky, promítnu si ho z entity na DTO, v UI jej upravím, a pak změny zase namapuju zpátky - AutoMapper umí například i synchronizovat kolekce podle primárních klíčů, dá se to různě nastavovat. 3) Další problém s entitami je lazy loading - občas je to úžasná věc, na druhou stranu když tu entitu zkusíte serializovat, tak při zapnutém lazy loadingu to rozbalí všechny property - nedávno jsme dělali jedné firmě konzultace ohledně výkonnosti aplikace a všimli jsme si, že posílají entity přes WCF - bylo to pomalé, protože se přenášelo strašně moc dat, de facto jim to vyserializovalo část databáze, která se k té entitě vázala. Jako jedinou výjimku, kdy entity mohou do UI, bych akceptoval u nějakých číselníků nebo primitivních tabulek, které mají 2 nebo 3 sloupce - tam bych na dělání DTO asi netrval, zvlášť pokud by do DTO bylo úplně stejné jako ta entita. Nicméně v praxi se mi spíš osvědčilo všechny tyhle číselníkové tabulky namapovat na jednu DTO třídu, díky čemuž můžu s číselníky v aplikaci pracovat obecně. Anebo bych to ještě pochopil u malých aplikací, které mají řekněme do 10 databázových tabulek - tam je možná tenhle cirkus zbytečný, pokud vím, že se ta aplikace nebude rozrůstat.
|