Jarda Jirava

Vývojář a architekt řešení postavených na technologii .net framework. Zabývám se jak vývojem webových aplikací za pomoci asp.net, tak také desktopových aplikací winform. Při návrhu řešení a samotném vývoji pak využívám dlouholetých zkušeností se zpracováním obchodní logiky a pravidel aplikací získaných z vývoje komerčních aplikací pro finanční a bankovní instituce.

Microsoft MVP

Microsoft MVP - Client App dev

Poslední příspěvky

15
Sep

Entity framework a asociace na DefiningQuery

Skončili prázdniny a jak to momentálně vypadá i pěkné počasí. A tak začíná být více času se věnovat také vývojařině a odpovědím na dotazy.

Sešlo se mi, jak přes kontaktní formulář tak i z konference, hned několik zajímavých otázek, na které přeci jen není takový prostor pro odpověď přímo do diskuze a nebo je škoda je nepublikovat veřejně.

Když to vezmu pěkně popořadě, tedy pozpátku, tak zajímavý dotaz se věnoval Entity frameworku. Zadání bylo celkem jednoduché a šlo o to, že existují dvě entity, které jsou na sebe navázány asociací ve vztahu 1-m. Cílem je vytvoření property, která tento vztah zachová, ale vybere pouze posledních n záznamů. Pavel dokonce poslal i řešení, jak toho docílit přímo v programu, ale toto řešení se mu nepozdávalo

postSet.select(a=> new (a, a.Comment.OrderBy(...).Take(5))

a já se mu příliš nedivím, neboť staví na anonymních třídách a přeci jen je třeba udělat něco navíc než by bylo nutné.

Vytvoření entity a asociace

Mnou navržené řešení spočívá ve vytvoření “nové” entity a definování asociace, nazvěme ji například LastComments, která bude obsahovat pouze posledních n záznamů.

Jak tedy budeme postupovat, abychom se dobrali k finálnímu výsledku. Předpokládejme, že máme databázi, která obsahuje dvě tabulky, jenž jsou na sebe navázány ve správném vztahu.

Založíme si tedy nový projekt a vložíme do něj ADO.NET Entity Data Model. Necháme se provést průvodcem, napojíme se na zdrojovou databázi a přidáme do modelu nám známé dvě entity.

EF-PostCommentEntity

Nový EntitySet s DefiningQuery

Nyní opustíme vizuálního návrháře a vrhneme se do tajů Storage modelu. Ano, je třeba přidat nový entity set, který bude představovat pouze poslední záznamy, které získává EF z databáze. Najdeme si tedy v SSDL schématu [1] EntitySet pro naši entitu komentářů (reprezentující stranu n vztahu) a tuto zkopírujeme,

EF-SSDLBeforeEdit

z takto vytvořeného EntitySetu smažeme atributy store:Type a Schema. Dále EntitySet přejmenujeme na LastComment a do elementu umístíme element DefiningQuery a zde zapíšeme náš dotaz do databáze. Ano, dotaz vypadá obdobně jako ten, který jsem zasílal jako řešení na hádanku Tomáše Herzega.

 EF-EntitySets

Soubor ještě nezavírejte, neboť je ještě třeba přidat Entitní typ, který vznikl dotazem do databáze. Najděte si tedy entitu komentářů, zkopírujte ji a změňte ji pouze název.

EF-LastComment

To by mělo být pro tuto chvíli vše co se týká ruční editace edmx souboru a je možné se vrátit do vizuálního návrháře.

Nová entita a asociace

Nyní už nám schází jen poslední krok a můžeme používat vlastnost LastComments a to je přidání nové entity a asociace. V modelu tedy vytvoříme novou entitu a přidáme do ní potřebné vlastnosti. Na entitě Post si dále zvolíme vytvoření nové asociace, zvolte jméno a obě strany vazby, nezapomeňte vhodně pojmenovat i tzv. Navigation Property.

EF-AllEntities

Nyní si vyberte tuto nově vytvořenou asociaci a v Mapping details okně provedeme mapování jak je naznačeno na obrázku.

EF-Mapping

Tím je naše mise u konce a nyní můžeme používat tuto vlastnost v projektu.

Nebezpečí číhá všude

Kdybych nyní skončil, asi byste mě po nějaké době používání přestali mít rádi. Proto raději hned upozorním na nebezpečí, které na váš číhá.

První upozornění se týká toho, že dané řešení tak jak je zapsáno je z pohledu vývojáře neměnné. Pokud vývojář začne používat vlastnost LastComments je to správně. Nebezpečí číhá v tom, že jsme náš Storage model upravili a svázali jsme jej tak s konkrétní implementací databázového stroje, v tomto případě MS SQL Serveru 2005 a vyšší. Pokud bychom chtěli migrovat na jiný databázový stroj, je třeba změnit, zdůrazním slovo pouze, SELECT dotaz ve Storage model schématu.

Druhým nebezpečenstvím pak je, že si nejsem jist správností navrženého řešení. Mohu se pouze domnívat, že je to takto správně a doufám, že mě někdo podpoří a navržený postup potvrdí.

Samozřejmě, pokud najdete lepší řešení, které se nebude spoléhat na definici nové entity a asociace, budu rád za jeho zveřejnění.

[1] abychom se přepnuli do možnosti editovat edmx soubor, je třeba kliknout pravým tlačítkem na modelu a zvolit volbu Open with … v dialogu poté zvolit xml editor.

Publikováno pod: Linq , .net technology
10
Jul

IQueryable do DataView

Prý nikde není k dispozici popis nebo dokonce kód pro vytvoření DataView z dotazu provedeného pomocí Linq to SQL. Tak jsem se jeden takový pokusil sestavit, byť by si určitě zasloužil ještě nějaké ty úpravy a vylepšení.

DataView ToDataView<T>(this IQueryable<T> query) where T: class

Tak toto je předpis dané extension metody, kterou je možné použít pro vytvoření DataView z dotazu. Celá metoda je velice jednoduchá a jistě ji pochopí každý, alespoń maličko zkušený vývojář. Pro ty ostatní jen malé připomenutí, jak celá metoda pracuje a provádí se převod. Nejdříve však samožná metoda.

using System.Data;
using System.Reflection;
using System.Collections;

public static class DataExtensions {
    public static DataView ToDataView<T>(this IQueryable<T> query) where T: class {
        DataTable dt = new DataTable();
        Type type = typeof(T);
        PropertyInfo[] pis = type.GetProperties();
        foreach (var item in pis) {
            if (!(typeof(ICollection).IsAssignableFrom(item.PropertyType))) {
                DataColumn dc = new DataColumn(item.Name, item.PropertyType);
                dt.Columns.Add(dc);
            }            
        }
        foreach (var item in query) {
            DataRow row = dt.NewRow();
            foreach (var pi in pis) {
                if (!(typeof(ICollection).IsAssignableFrom(pi.PropertyType))) {
                    row[pi.Name] = pi.GetValue(item, null);
                }
            }
            dt.Rows.Add(row);
        }
        return new DataView(dt);
    }
}

Nejdříve je třeba zajistit vytvoření odpovídajících sloupečků. To se provede iterováním přes všechny viditelné vlastnosti třídy s tím, že vynechávám ze seznamu kolekce (EntitySety). Pro jistotu jsem však použil jen rozhraní ICollection, tak aby byly vynechány případné dodatečně definované kolekce.

Následně se již vytvářejí řádky a pomoci Reflection se plní jednotlivé řádky hodnotamy z dotazu.

Posledním krokem je poté již jen samotné vrácení DataView na výstupu z metody.

Publikováno pod: code snippet , Linq , .net technology
21
Apr

LINQ to SQL a BindingSource

Je to jen pár měsíců, co je na světě .net framework verze 3.5 a s ním spoustu cukrátek, které potěší nejednoho vývojáře na .netím frameworku. Někteří z nás sice vzhlížejí ještě kousek dál - třeba k ADO.NET Entity Frameworku, ale pro ty, kteřím stačí současný stav a pohrávají si s LINQ to SQL, tu mám jeden tip.

Přestože jsme na builderu nedokázali odhalit přesnou příčinu nefunkčnosti nad položenou otázkou LINQ + SQL + datagridview - uložení změn do db, snažil jsem se přijít na důvody, proč u tazatele daná kombinace nebyla funkční. Přiznám se hned na začátku, že jsem na rozumné vysvětlení nepřišel.

Jak propojit data na DataGridView

Vhodnou cestou jak napojit data z datového zdroje, v tomto případě tedy entit vytvořených pomocí LINQ to SQL, na DataGridView je použít objektu BindingSource. Při nastavení vlastnosti DataSource dojde k provázání dat se zobrazovacím prvkem a v případě, že máme povoleno na DataGridView přidávání, případně mazání řádků máme vše hotovo.

Skutečně nám tedy stačí, ve vhodnou chvíli volat událost SubmitChanges nad stejnou instancí DataContextu, jakou jsme naplnily BindingSource a o vše se postará interní logika. Není tak třeba registrovat se k žádným událostem - snad až na jedinou a tou je událost pro přidání nového záznamu AddingNew, a to jen v případě, že požadujeme mít nově přidaný objekt uživatelsky nainicializovaný.

Stejně tak není třeba notifikovat DataContext, lépe řečeno Table<TEntity> kolekci o nově přidaných objektech pomocí metody InsertOnSubmit, stejně tak jako psát kód navíc, abychom zjistili odstraňovanou položku z kolekce a tu mohli předat do metody DeleteOnSubmit.

Ukázka

Jak tedy může taková jednoduchá práce s DataGridView a jeho napojením na data vypadat? Následující část kódu je skutečné minimum:

// metoda pro získání dat z datového zdroje LINQ to SQL
private IEnumerable provideData() {
 var result = from data in _db.TestRows
  select data;
 return result;
}
// metoda pro uložení editovaných dat
private void saveClick(object sender, EventArgs e) {
  _db.SubmitChanges();
}

... // tam kde potrebujeme nahrát data a napojit je na zobrazovací prvek
bindingSource.DataSource = provideData();
...
Publikováno pod: Linq , builder.cz , .net technology