여러 결과 집합이 있는 저장 프로시저
저장 프로시저를 사용하는 경우 둘 이상의 결과 집합을 반환해야 하는 경우가 있습니다. 이 시나리오는 일반적으로 단일 화면을 구성하는 데 필요한 데이터베이스 왕복 수를 줄이는 데 사용됩니다. EF5 이전에는 Entity Framework에서 저장 프로시저를 호출할 수 있지만 첫 번째 결과 집합만 호출 코드로 반환합니다.
이 문서에서는 Entity Framework의 저장 프로시저에서 둘 이상의 결과 집합에 액세스하는 데 사용할 수 있는 두 가지 방법을 보여줍니다. 하나는 코드만 사용하고 Code First 및 EF Designer 모두에서 작동하는 방법이며 다른 하나는 EF 디자이너에서만 작동하는 방법입니다. 이에 대한 도구 및 API 지원은 이후 버전의 Entity Framework에서 개선되어야 합니다.
모델
이 문서의 예제에서는 블로그에 게시물이 많고 게시물이 단일 블로그에 속하는 기본 블로그 및 게시물 모델을 사용합니다. 다음과 같이 모든 블로그와 게시물을 반환하는 저장 프로시저를 데이터베이스에 사용합니다.
CREATE PROCEDURE [dbo].[GetAllBlogsAndPosts]
AS
SELECT * FROM dbo.Blogs
SELECT * FROM dbo.Posts
코드를 사용하여 여러 결과 집합에 액세스
코드를 통해 원시 SQL 명령을 실행하여 저장 프로시저를 실행할 수 있습니다. 이러한 접근 방식의 장점은 Code First 및 EF 디자이너 모두에서 작동한다는 것입니다.
여러 결과 집합이 작동하려면 IObjectContextAdapter 인터페이스를 사용하여 ObjectContext API로 삭제해야 합니다.
ObjectContext가 있으면 좌표 이동 메서드를 사용하여 저장 프로시저의 결과를 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();
}
}
좌표 이동 메서드는 프로시저, EntitySet 이름 및 MergeOption을 실행할 때 수신되는 판독기를 허용합니다. EntitySet 이름은 파생 컨텍스트의 DbSet 속성과 동일합니다. MergeOption 열거형은 동일한 엔터티가 메모리에 이미 있는 경우 결과를 처리하는 방법을 제어합니다.
여기서는 NextResult를 호출하기 전에 블로그 컬렉션을 반복합니다. 이는 다음 결과 집합으로 이동하기 전에 첫 번째 결과 집합을 사용해야 하므로 위의 코드를 고려할 때 중요합니다.
두 개의 좌표 이동 메서드가 호출되면 블로그 및 게시물 엔터티는 다른 엔터티와 동일한 방식으로 EF에서 추적되므로 수정하거나 삭제하고 정상적으로 저장할 수 있습니다.
참고 항목
EF는 좌표 이동 메서드를 사용하여 엔터티를 만들 때 매핑을 고려하지 않습니다. 단순히 결과 집합의 열 이름과 클래스의 속성 이름과 일치합니다.
참고 항목
지연 로드를 사용하도록 설정한 경우 블로그 엔터티 중 하나의 게시물 속성에 액세스하면 EF는 데이터베이스에 연결하여 이미 모든 게시물을 로드했음에도 불구하고 모든 게시물을 느리게 로드합니다. 이는 EF가 모든 게시물을 로드했는지 아니면 데이터베이스에 더 많은 게시물이 있는지 여부를 알 수 없기 때문입니다. 이를 방지하려면 지연 로드를 사용하지 않도록 설정해야 합니다.
EDMX에서 구성된 여러 결과 집합
참고 항목
EDMX에서 여러 결과 집합을 구성할 수 있도록 .NET Framework 4.5를 대상으로 해야 합니다. .NET 4.0을 대상으로 하는 경우 이전 섹션에 표시된 코드 기반 메서드를 사용할 수 있습니다.
EF 디자이너를 사용하는 경우 반환될 다양한 결과 집합에 대해 알 수 있도록 모델을 수정할 수도 있습니다. 미리 알아야 할 한 가지는 도구가 여러 결과 집합을 인식하지 않으므로 edmx 파일을 수동으로 편집해야 한다는 것입니다. 이와 같이 edmx 파일을 편집하면 작동은 하지만 VS에서 모델의 유효성 검사도 중단됩니다. 따라서 모델의 유효성을 검사하면 항상 오류가 발생합니다.
이를 수행하려면 단일 결과 집합 쿼리와 마찬가지로 저장 프로시저를 모델에 추가해야 합니다.
그러려면 모델을 마우스 오른쪽 단추로 클릭하고 다음으로 열기를 선택한 다음 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에서는 여러 결과 집합에 대한 지원이 개선될 예정이며 이 문서의 단계를 더 이상 수행할 필요가 없습니다.
.NET