Uložené procedury s více sadami výsledků

Někdy při použití uložených procedur budete muset vrátit více než jednu sadu výsledků. Tento scénář se běžně používá ke snížení počtu odezvy databáze potřebných k vytvoření jedné obrazovky. Před EF5 by Entity Framework umožňoval volání uložené procedury, ale vrátilo by pouze první sadu výsledků volajícího kódu.

Tento článek vám ukáže dva způsoby, jak můžete použít pro přístup k více než jedné sadě výsledků z uložené procedury v Entity Frameworku. Ten, který používá pouze kód a funguje s aplikací Code first i EF Designer, a ten, který funguje jenom s EF Designerem. Podpora nástrojů a rozhraní API by se měla v budoucích verzích entity Framework zlepšit.

Model

Příklady v tomto článku používají základní model blogů a příspěvků, kde blog obsahuje mnoho příspěvků a příspěvek patří do jednoho blogu. Použijeme uloženou proceduru v databázi, která vrátí všechny blogy a příspěvky, například takto:

    CREATE PROCEDURE [dbo].[GetAllBlogsAndPosts]
    AS
        SELECT * FROM dbo.Blogs
        SELECT * FROM dbo.Posts

Přístup k více sadám výsledků pomocí kódu

Ke spuštění uložené procedury můžeme použít kód k vydání nezpracovaného příkazu SQL. Výhodou tohoto přístupu je to, že funguje s programem Code first a EF Designerem.

Aby bylo možné získat více sad výsledků, musíme pomocí rozhraní IObjectContextAdapter rozhraní vyřadit do rozhraní ObjectContext API.

Jakmile máme ObjectContext, můžeme použít Translate metodu k překladu výsledků naší uložené procedury do entit, které lze sledovat a používat v EF jako normální. Následující ukázka kódu ukazuje tuto akci.

    using (var db = new BloggingContext())
    {
        // If using Code First we need to make sure the model is built before we open the connection
        // This isn't required for models created with the EF Designer
        db.Database.Initialize(force: false);

        // Create a SQL command to execute the sproc
        var cmd = db.Database.Connection.CreateCommand();
        cmd.CommandText = "[dbo].[GetAllBlogsAndPosts]";

        try
        {

            db.Database.Connection.Open();
            // Run the sproc
            var reader = cmd.ExecuteReader();

            // Read Blogs from the first result set
            var blogs = ((IObjectContextAdapter)db)
                .ObjectContext
                .Translate<Blog>(reader, "Blogs", MergeOption.AppendOnly);   


            foreach (var item in blogs)
            {
                Console.WriteLine(item.Name);
            }        

            // Move to second result set and read Posts
            reader.NextResult();
            var posts = ((IObjectContextAdapter)db)
                .ObjectContext
                .Translate<Post>(reader, "Posts", MergeOption.AppendOnly);


            foreach (var item in posts)
            {
                Console.WriteLine(item.Title);
            }
        }
        finally
        {
            db.Database.Connection.Close();
        }
    }

Metoda Translate přijímá čtenáře, který jsme obdrželi při spuštění procedury, název EntitySet a MergeOption. Název EntitySet bude stejný jako dbSet vlastnost v odvozený kontext. Výčet MergeOption řídí způsob zpracování výsledků, pokud stejná entita již existuje v paměti.

Zde iterujeme kolekci blogů před voláním NextResult, to je důležité vzhledem k výše uvedenému kódu, protože první sada výsledků musí být spotřebována před přechodem na další sadu výsledků.

Jakmile jsou tyto dvě metody překladu volána, entity Blog a Post jsou sledovány EF stejným způsobem jako jakákoli jiná entita, takže je možné upravit nebo odstranit a uložit jako normální.

Poznámka

EF při vytváření entit pomocí metody Translate nezohlední žádné mapování. Jednoduše bude odpovídat názvům sloupců ve výsledné sadě s názvy vlastností ve vašich třídách.

Poznámka

Pokud máte povolené opožděné načítání, přístup k post vlastnost na jedné z entit blogu, EF se připojí k databázi, aby lazily načíst všechny příspěvky, i když jsme je už načetli všechny. Důvodem je to, že EF nemůže zjistit, jestli jste načetli všechny příspěvky nebo jestli je v databázi více. Pokud se tomu chcete vyhnout, budete muset zakázat opožděné načítání.

Více sad výsledků s nakonfigurovaným v EDMX

Poznámka

Abyste mohli v EDMX nakonfigurovat více sad výsledků, musíte cílit na rozhraní .NET Framework 4.5. Pokud cílíte na .NET 4.0, můžete použít metodu založenou na kódu uvedenou v předchozí části.

Pokud používáte EF Designer, můžete také upravit model tak, aby věděl o různých sadách výsledků, které budou vráceny. Jednou z věcí, kterou byste měli vědět před rukou, je, že nástroje nejsou více sad výsledků, takže budete muset ručně upravit soubor edmx. Úprava souboru edmx takto bude fungovat, ale zároveň přeruší ověření modelu ve VS. Pokud tedy model ověříte, vždy se zobrazí chyby.

  • Abyste to mohli udělat, musíte do modelu přidat uloženou proceduru stejně jako pro jeden dotaz sady výsledků.

  • Až to budete mít, budete muset kliknout pravým tlačítkem myši na model a vybrat Otevřít s.. pak Xml

    Open As

Jakmile máte model otevřený jako XML, musíte provést následující kroky:

  • Najděte v modelu komplexní typ a import funkcí:
    <!-- CSDL content -->
    <edmx:ConceptualModels>

    ...

      <FunctionImport Name="GetAllBlogsAndPosts" ReturnType="Collection(BlogModel.GetAllBlogsAndPosts_Result)" />

    ...

      <ComplexType Name="GetAllBlogsAndPosts_Result">
        <Property Type="Int32" Name="BlogId" Nullable="false" />
        <Property Type="String" Name="Name" Nullable="false" MaxLength="255" />
        <Property Type="String" Name="Description" Nullable="true" />
      </ComplexType>

    ...

    </edmx:ConceptualModels>

 

  • Odebrání komplexního typu
  • Aktualizujte import funkce tak, aby se mapuje na vaše entity, v našem případě bude vypadat takto:
    <FunctionImport Name="GetAllBlogsAndPosts">
      <ReturnType EntitySet="Blogs" Type="Collection(BlogModel.Blog)" />
      <ReturnType EntitySet="Posts" Type="Collection(BlogModel.Post)" />
    </FunctionImport>

To říká modelu, že uložená procedura vrátí dvě kolekce, jednu z položek blogu a jednu z položek příspěvku.

  • Vyhledejte prvek mapování funkcí:
    <!-- C-S mapping content -->
    <edmx:Mappings>

    ...

      <FunctionImportMapping FunctionImportName="GetAllBlogsAndPosts" FunctionName="BlogModel.Store.GetAllBlogsAndPosts">
        <ResultMapping>
          <ComplexTypeMapping TypeName="BlogModel.GetAllBlogsAndPosts_Result">
            <ScalarProperty Name="BlogId" ColumnName="BlogId" />
            <ScalarProperty Name="Name" ColumnName="Name" />
            <ScalarProperty Name="Description" ColumnName="Description" />
          </ComplexTypeMapping>
        </ResultMapping>
      </FunctionImportMapping>

    ...

    </edmx:Mappings>
  • Nahraďte mapování výsledků jednou pro každou vrácenou entitu, například následující:
    <ResultMapping>
      <EntityTypeMapping TypeName ="BlogModel.Blog">
        <ScalarProperty Name="BlogId" ColumnName="BlogId" />
        <ScalarProperty Name="Name" ColumnName="Name" />
        <ScalarProperty Name="Description" ColumnName="Description" />
      </EntityTypeMapping>
    </ResultMapping>
    <ResultMapping>
      <EntityTypeMapping TypeName="BlogModel.Post">
        <ScalarProperty Name="BlogId" ColumnName="BlogId" />
        <ScalarProperty Name="PostId" ColumnName="PostId"/>
        <ScalarProperty Name="Title" ColumnName="Title" />
        <ScalarProperty Name="Text" ColumnName="Text" />
      </EntityTypeMapping>
    </ResultMapping>

Sady výsledků je také možné mapovat na komplexní typy, například sady výsledků vytvořené ve výchozím nastavení. K tomu vytvoříte nový komplexní typ místo jejich odebrání a použijete komplexní typy všude, kde jste použili názvy entit v příkladech výše.

Po změně těchto mapování můžete model uložit a spustit následující kód, který použije uloženou proceduru:

    using (var db = new BlogEntities())
    {
        var results = db.GetAllBlogsAndPosts();

        foreach (var result in results)
        {
            Console.WriteLine("Blog: " + result.Name);
        }

        var posts = results.GetNextResult<Post>();

        foreach (var result in posts)
        {
            Console.WriteLine("Post: " + result.Title);
        }

        Console.ReadLine();
    }

Poznámka

Pokud ručně upravíte soubor edmx pro váš model, přepíše se, pokud model znovu vygenerujete z databáze.

Souhrn

Zde jsme ukázali dvě různé metody přístupu k více sadám výsledků pomocí Entity Frameworku. Obě jsou stejně platné v závislosti na vaší situaci a preferencích a měli byste zvolit ten, který se zdá být pro vaše okolnosti nejvhodnější. Plánuje se, že podpora více sad výsledků bude v budoucích verzích entity Framework vylepšena a že provádění kroků v tomto dokumentu už nebude nutné.