具有多個結果集的預存程式
有時候使用預存程式時,您必須傳回一個以上的結果集。 此案例通常用於減少撰寫單一畫面所需的資料庫來回行程數目。 在 EF5 之前,Entity Framework 會允許呼叫預存程式,但只會將第一個結果集傳回給呼叫端程式碼。
本文將說明兩種方式,可用來從 Entity Framework 中的預存程式存取多個結果集。 一個只使用程式碼,並且先與程式碼搭配 EF 設計工具運作,另一個只與 EF Designer 搭配運作。 此工具和 API 支援應該在未來的 Entity Framework 版本中改善。
Model
本文中的範例使用基本的部落格和文章模型,其中部落格有許多文章,而文章屬於單一部落格。 我們將在資料庫中使用預存程式來傳回所有部落格和文章,如下所示:
CREATE PROCEDURE [dbo].[GetAllBlogsAndPosts]
AS
SELECT * FROM dbo.Blogs
SELECT * FROM dbo.Posts
使用程式碼存取多個結果集
我們可以使用程式碼發出原始 SQL 命令來執行預存程式。 這個方法的優點是,它會先與程式碼和 EF 設計工具搭配使用。
若要讓多個結果集能夠運作,我們需要使用 IObjectCoNtextAdapter 介面卸載至 ObjectCoNtext API。
一旦有了 ObjectCoNtext,我們就可以使用 Translate 方法,將預存程式的結果轉譯成可以正常追蹤及使用於 EF 中的實體。 下列程式碼範例示範此動作。
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();
}
}
Translate 方法會接受我們在執行程式、EntitySet 名稱和 MergeOption 時收到的讀取器。 EntitySet 名稱會與您衍生內容上的 DbSet 屬性相同。 MergeOption 列舉會控制當記憶體中已有相同實體時,結果的處理方式。
在此,我們會在呼叫 NextResult 之前逐一查看部落格集合,這在上述程式碼中很重要,因為必須先取用第一個結果集,才能移至下一個結果集。
呼叫這兩個翻譯方法之後,EF 會以與任何其他實體相同的方式追蹤 Blog 和 Post 實體,以便修改或刪除並儲存為一般。
注意
EF 不會在使用 Translate 方法建立實體時考慮任何對應。 它只會比對結果集中的資料行名稱與類別上的屬性名稱。
注意
如果您已啟用延遲載入,請在其中一個部落格實體上存取文章屬性,則 EF 會連線到資料庫以延遲載入所有文章,即使我們已經全部載入。 這是因為 EF 無法知道您是否已載入所有文章,或資料庫中是否有更多文章。 如果您想要避免這種情況,則必須停用延遲載入。
在 EDMX 中設定的多個結果集
注意
您必須以 .NET Framework 4.5 為目標,才能在 EDMX 中設定多個結果集。 如果您要以 .NET 4.0 為目標,您可以使用上一節所示的程式碼型方法。
如果您使用 EF 設計工具,您也可以修改模型,讓它知道將傳回的不同結果集。 手前要知道的一件事是工具不是多個結果集感知,因此您必須手動編輯 edmx 檔案。 編輯這類 edmx 檔案會正常運作,但它也會中斷 VS 中模型的驗證。 因此,如果您驗證模型,您一律會收到錯誤。
若要這樣做,您必須將預存程式新增至模型,就像單一結果集查詢一樣。
完成此動作之後,您必須以滑鼠右鍵按一下模型,然後選取 [開啟 With]。然後 選取 [Xml]。
將模型開啟為 XML 之後,您必須執行下列步驟:
- 在您的模型中尋找複雜類型和函式匯入:
<!-- 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>
- 移除複雜類型
- 更新函式匯入,使其對應至您的實體,在我們的案例中,它看起來會像下面這樣:
<FunctionImport Name="GetAllBlogsAndPosts">
<ReturnType EntitySet="Blogs" Type="Collection(BlogModel.Blog)" />
<ReturnType EntitySet="Posts" Type="Collection(BlogModel.Post)" />
</FunctionImport>
這會告訴模型,預存程式會傳回兩個集合,其中一個部落格專案和一個文章專案。
- 尋找函式對應專案:
<!-- 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>
- 將結果對應取代為所傳回的每個實體的一個,如下所示:
<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>
您也可以將結果集對應至複雜類型,例如預設建立的結果集。 若要這樣做,您可以建立新的複雜類型,而不是移除它們,並在您在上述範例中使用機構名稱的任何地方使用複雜類型。
變更這些對應之後,您就可以儲存模型並執行下列程式碼來使用預存程式:
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();
}
注意
如果您手動編輯模型的 edmx 檔案,如果您曾經從資料庫重新產生模型,就會覆寫該檔案。
摘要
在這裡,我們示範了使用 Entity Framework 存取多個結果集的兩種不同的方法。 這兩者都同樣有效,視您的情況和喜好設定而定,您應該選擇最適合您的情況。 計畫在未來的 Entity Framework 版本中,將改善對多個結果集的支援,而且不再需要執行本檔中的步驟。