Procedury składowane z wieloma zestawami wyników

Czasami w przypadku korzystania z procedur składowanych należy zwrócić więcej niż jeden zestaw wyników. Ten scenariusz jest często używany do zmniejszenia liczby rund bazy danych wymaganych do utworzenia pojedynczego ekranu. Przed ef5 program Entity Framework zezwoliłby na wywoływanie procedury składowanej, ale zwracałby tylko pierwszy zestaw wyników do kodu wywołującego.

W tym artykule przedstawiono dwa sposoby uzyskiwania dostępu do więcej niż jednego zestawu wyników z procedury składowanej w programie Entity Framework. Ten, który używa tylko kodu i współpracuje zarówno z kodem Code first, jak i EF Projektant i z programem EF, który działa tylko z Projektant EF. Obsługa narzędzi i interfejsów API powinna zostać ulepszona w przyszłych wersjach programu Entity Framework.

Model

W przykładach w tym artykule użyto podstawowego modelu blogów i wpisów, w którym blog zawiera wiele wpisów, a wpis należy do jednego bloga. Użyjemy procedury składowanej w bazie danych, która zwraca wszystkie blogi i wpisy w następujący sposób:

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

Uzyskiwanie dostępu do wielu zestawów wyników za pomocą kodu

Możemy wykonać kod, aby wydać nieprzetworzone polecenie SQL w celu wykonania naszej procedury składowanej. Zaletą tego podejścia jest to, że działa zarówno z kodem first, jak i ef Projektant.

Aby uzyskać wiele zestawów wyników, musimy porzucić interfejs API ObjectContext przy użyciu interfejsu IObjectContextAdapter.

Gdy mamy obiekt ObjectContext, możemy użyć metody Translate, aby przetłumaczyć wyniki naszej procedury składowanej na jednostki, które można śledzić i używać w programie EF w zwykły sposób. Poniższy przykład kodu demonstruje to w akcji.

    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 akceptuje czytelnika otrzymanego podczas wykonywania procedury, nazwy EntitySet i scalaniaOption. Nazwa elementu EntitySet będzie taka sama jak właściwość DbSet w kontekście pochodnym. Wyliczenie MergeOption określa sposób obsługi wyników, jeśli ta sama jednostka już istnieje w pamięci.

W tym miejscu iterujemy po kolekcji blogów przed wywołaniem metody NextResult. Jest to ważne, biorąc pod uwagę powyższy kod, ponieważ przed przejściem do następnego zestawu wyników należy użyć pierwszego zestawu wyników.

Po wywołaniu dwóch metod tłumaczenia jednostki Blog i Post są śledzone przez program EF w taki sam sposób jak każda inna jednostka i dlatego można je modyfikować lub usuwać i zapisywać w zwykły sposób.

Uwaga

Program EF nie uwzględnia mapowania podczas tworzenia jednostek przy użyciu metody Translate. Będzie ona po prostu dopasowywać nazwy kolumn w zestawie wyników z nazwami właściwości w klasach.

Uwaga

Jeśli masz włączone opóźnienie ładowania, uzyskiwanie dostępu do właściwości postów w jednej z jednostek blogu, program EF połączy się z bazą danych, aby leniwie załadować wszystkie wpisy, mimo że wszystkie zostały już załadowane. Dzieje się tak, ponieważ program EF nie może wiedzieć, czy załadowano wszystkie wpisy, czy też nie ma więcej w bazie danych. Jeśli chcesz tego uniknąć, musisz wyłączyć leniwe ładowanie.

Wiele zestawów wyników ze skonfigurowanymi w programie EDMX

Uwaga

Aby można było skonfigurować wiele zestawów wyników w programie EDMX, należy kierować program .NET Framework 4.5. Jeśli używasz platformy .NET 4.0, możesz użyć metody opartej na kodzie pokazanej w poprzedniej sekcji.

Jeśli używasz Projektant EF, możesz również zmodyfikować model, aby wiedział o różnych zestawach wyników, które zostaną zwrócone. Jedną z rzeczy, które należy znać przed ręką, jest to, że narzędzia nie są świadome wielu zestawów wyników, więc trzeba będzie ręcznie edytować plik edmx. Edytowanie pliku edmx w ten sposób będzie działać, ale również spowoduje przerwanie walidacji modelu w programie VS. Dlatego jeśli zweryfikujesz model, zawsze wystąpią błędy.

  • Aby to zrobić, należy dodać procedurę składowaną do modelu, tak jak w przypadku pojedynczego zapytania zestawu wyników.

  • Po utworzeniu tego elementu musisz kliknąć prawym przyciskiem myszy model i wybrać polecenie Otwórz za pomocą. Następnie kliknij polecenie Xml.

    Open As

Po otwarciu modelu jako XML należy wykonać następujące czynności:

  • Znajdź typ złożony i import funkcji w modelu:
    <!-- 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>

 

  • Usuwanie typu złożonego
  • Zaktualizuj importowanie funkcji tak, aby była mapowane na jednostki. W naszym przypadku będzie ona wyglądać następująco:
    <FunctionImport Name="GetAllBlogsAndPosts">
      <ReturnType EntitySet="Blogs" Type="Collection(BlogModel.Blog)" />
      <ReturnType EntitySet="Posts" Type="Collection(BlogModel.Post)" />
    </FunctionImport>

Informuje to model, że procedura składowana zwróci dwie kolekcje: jedną z wpisów w blogu i jedną z wpisów.

  • Znajdź element mapowania funkcji:
    <!-- 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>
  • Zastąp mapowanie wyników jednym dla każdej zwracanej jednostki, na przykład:
    <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>

Istnieje również możliwość mapowania zestawów wyników na typy złożone, takie jak utworzone domyślnie. Aby to zrobić, należy utworzyć nowy typ złożony, zamiast ich usuwać i używać typów złożonych wszędzie, gdzie użyto nazw jednostek w powyższych przykładach.

Po zmianie tych mapowań można zapisać model i wykonać następujący kod, aby użyć procedury składowanej:

    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();
    }

Uwaga

Jeśli ręcznie edytujesz plik edmx dla modelu, zostanie on zastąpiony, jeśli kiedykolwiek zregenerujesz model z bazy danych.

Podsumowanie

W tym miejscu przedstawiono dwie różne metody uzyskiwania dostępu do wielu zestawów wyników przy użyciu platformy Entity Framework. Oba z nich są równie ważne w zależności od sytuacji i preferencji i należy wybrać ten, który wydaje się najlepszy dla Twoich okoliczności. Planowane jest, że obsługa wielu zestawów wyników zostanie ulepszona w przyszłych wersjach programu Entity Framework i wykonanie kroków opisanych w tym dokumencie nie będzie już konieczne.