비동기 쿼리 및 저장

메모

EF6 이후 버전만 - 이 페이지에서 설명하는 기능, API 등이 Entity Framework 6에 도입되었습니다. 이전 버전을 사용하는 경우 일부 또는 모든 정보가 적용되지 않습니다.

EF6은 .NET 4.5에서 도입된 비 동기 및 대기 키워드 를 사용하여 비동기 쿼리 및 저장에 대한 지원을 도입했습니다. 모든 애플리케이션이 비동기 기능을 활용하는 것은 아니지만 장기 실행, 네트워크 또는 I/O 바인딩된 작업을 처리할 때 클라이언트 응답성 및 서버 확장성을 향상시키는 데 사용할 수 있습니다.

실제로 비동기를 사용해야 하는 경우

이 연습의 목적은 비동기 프로그램 실행과 동기 프로그램 실행의 차이를 쉽게 관찰할 수 있는 방식으로 비동기 개념을 도입하는 것입니다. 이 연습은 비동기 프로그래밍이 이점을 제공하는 주요 시나리오를 설명하기 위한 것이 아닙니다.

비동기 프로그래밍은 주로 관리되는 스레드에서 컴퓨팅 시간이 필요하지 않은 작업을 기다리는 동안 현재 관리되는 스레드(.NET 코드를 실행하는 스레드)를 해제하여 다른 작업을 수행하는 데 중점을 줍니다. 예를 들어 데이터베이스 엔진이 쿼리를 처리하는 동안 .NET 코드에서 수행할 작업은 없습니다.

클라이언트 애플리케이션(WinForms, WPF 등)에서는 비동기 작업이 수행되는 동안 현재 스레드를 사용하여 UI 응답성을 유지할 수 있습니다. 서버 애플리케이션(ASP.NET 등)에서 스레드를 사용하여 들어오는 다른 요청을 처리할 수 있습니다. 이렇게 하면 메모리 사용량이 줄어들거나 서버의 처리량이 증가할 수 있습니다.

비동기를 사용하는 대부분의 애플리케이션에서는 눈에 띄는 이점이 없으며 심지어 해로울 수도 있습니다. 테스트, 프로파일링 및 상식을 사용하여 커밋하기 전에 특정 시나리오에서 비동기의 영향을 측정합니다.

다음은 비동기에 대해 알아볼 수 있는 몇 가지 추가 리소스입니다.

모델 만들기

Code First 워크플로를 사용하여 모델을 만들고 데이터베이스를 생성합니다. 그러나 비동기 기능은 EF 디자이너를 사용하여 만든 모델을 포함하여 모든 EF 모델에서 작동합니다.

  • 콘솔 애플리케이션 만들기 및 AsyncDemo 호출
  • EntityFramework NuGet 패키지 추가
    • 솔루션 탐색기에서 AsyncDemo 프로젝트를 마우스 오른쪽 단추로 클릭합니다.
    • NuGet 패키지 관리 선택...
    • NuGet 패키지 관리 대화 상자에서 온라인 탭을 선택하고 EntityFramework 패키지를 선택합니다.
    • 설치를 클릭합니다.
  • 다음 구현을 사용하여 Model.cs 클래스 추가
    using System.Collections.Generic;
    using System.Data.Entity;

    namespace AsyncDemo
    {
        public class BloggingContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
            public DbSet<Post> Posts { get; set; }
        }

        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }

            public virtual List<Post> Posts { get; set; }
        }

        public class Post
        {
            public int PostId { get; set; }
            public string Title { get; set; }
            public string Content { get; set; }

            public int BlogId { get; set; }
            public virtual Blog Blog { get; set; }
        }
    }

 

동기 프로그램 만들기

이제 EF 모델이 있으므로 이를 사용하여 일부 데이터 액세스를 수행하는 코드를 작성해 보겠습니다.

  • Program.cs 내용을 다음 코드로 바꿉니다.
    using System;
    using System.Linq;

    namespace AsyncDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                PerformDatabaseOperations();

                Console.WriteLine("Quote of the day");
                Console.WriteLine(" Don't worry about the world coming to an end today... ");
                Console.WriteLine(" It's already tomorrow in Australia.");

                Console.WriteLine();
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }

            public static void PerformDatabaseOperations()
            {
                using (var db = new BloggingContext())
                {
                    // Create a new blog and save it
                    db.Blogs.Add(new Blog
                    {
                        Name = "Test Blog #" + (db.Blogs.Count() + 1)
                    });
                    Console.WriteLine("Calling SaveChanges.");
                    db.SaveChanges();
                    Console.WriteLine("SaveChanges completed.");

                    // Query for all blogs ordered by name
                    Console.WriteLine("Executing query.");
                    var blogs = (from b in db.Blogs
                                orderby b.Name
                                select b).ToList();

                    // Write all blogs out to Console
                    Console.WriteLine("Query completed with following results:");
                    foreach (var blog in blogs)
                    {
                        Console.WriteLine(" " + blog.Name);
                    }
                }
            }
        }
    }

이 코드는 데이터베이스에 PerformDatabaseOperations블로그 를 저장한 다음 데이터베이스에서 모든 블로그를 검색하여 콘솔에 출력하는 메서드를 호출합니다. 이 후에 프로그램은 콘솔에 오늘의 견적을 씁니다.

코드는 동기적이므로 프로그램을 실행할 때 다음 실행 흐름을 관찰할 수 있습니다.

  1. SaveChanges 에서는 새 블로그 를 데이터베이스에 푸시하기 시작합니다.
  2. SaveChanges 완료
  3. 모든 블로그에 대한 쿼리 데이터베이스로 전송됩니다.
  4. 쿼리 반환 및 결과가 콘솔에 기록됩니다.
  5. 오늘의 명언은 콘솔에 기록됩니다.

출력 동기화  

 

비동기화

이제 프로그램이 실행 중이므로 새로운 async 및 await 키워드를 사용할 수 있습니다. Program.cs 다음과 같은 변경 사항을 적용했습니다.

  1. 줄 2: 네임스페이스에 대한 System.Data.Entity using 문을 사용하면 EF 비동기 확장 메서드에 액세스할 수 있습니다.
  2. 줄 4: 네임스페이스 System.Threading.Tasks에 대한 using 문을 사용하면 Task 형식을 사용할 수 있습니다.
  3. 줄 12 및 18: 우리는 PerformSomeDatabaseOperations의 진행률을 모니터링하는 작업을 캡처하고, 프로그램의 모든 작업이 완료되면 이 작업이 완료되도록 프로그램 실행을 차단합니다.
  4. PerformSomeDatabaseOperations을(를) async으로 표시하도록 업데이트하고 Task을(를) 반환하도록 했습니다.
  5. 35줄: 이제 비동기 버전을 SaveChanges 호출하고 완료되기를 기다리고 있습니다.
  6. 줄 42: 이제 비동기 버전을 ToList 호출하고 결과를 기다리고 있습니다.

네임스페이스에서 사용 가능한 확장 메서드 System.Data.Entity 의 포괄적인 목록은 클래스를 QueryableExtensions 참조하세요. using 문에도 using System.Data.Entity를 추가해야 합니다.

    using System;
    using System.Data.Entity;
    using System.Linq;
    using System.Threading.Tasks;

    namespace AsyncDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                var task = PerformDatabaseOperations();

                Console.WriteLine("Quote of the day");
                Console.WriteLine(" Don't worry about the world coming to an end today... ");
                Console.WriteLine(" It's already tomorrow in Australia.");

                task.Wait();

                Console.WriteLine();
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }

            public static async Task PerformDatabaseOperations()
            {
                using (var db = new BloggingContext())
                {
                    // Create a new blog and save it
                    db.Blogs.Add(new Blog
                    {
                        Name = "Test Blog #" + (db.Blogs.Count() + 1)
                    });
                    Console.WriteLine("Calling SaveChanges.");
                    await db.SaveChangesAsync();
                    Console.WriteLine("SaveChanges completed.");

                    // Query for all blogs ordered by name
                    Console.WriteLine("Executing query.");
                    var blogs = await (from b in db.Blogs
                                orderby b.Name
                                select b).ToListAsync();

                    // Write all blogs out to Console
                    Console.WriteLine("Query completed with following results:");
                    foreach (var blog in blogs)
                    {
                        Console.WriteLine(" - " + blog.Name);
                    }
                }
            }
        }
    }

이제 코드가 비동기이므로 프로그램을 실행할 때 다른 실행 흐름을 관찰할 수 있습니다.

  1. SaveChanges 에서는 새 블로그 를 데이터베이스에 푸시하기 시작합니다.
    명령이 데이터베이스로 전송되면 현재 관리되는 스레드에서 더 이상 컴퓨팅 시간이 필요하지 않습니다. 메서드가 PerformDatabaseOperations 반환되고(실행이 완료되지 않았음에도 불구하고) Main 메서드의 프로그램 흐름이 계속됩니다.
  2. 오늘의 명언이 콘솔에 출력됩니다.
    Main 메서드에서 수행할 작업이 더 이상 없으므로 데이터베이스 작업이 완료될 때까지 호출에서 Wait 관리되는 스레드가 차단됩니다. 완료되면 우리 PerformDatabaseOperations의 나머지 부분이 실행됩니다.
  3. SaveChanges 완료
  4. 모든 블로그에 대한 쿼리 데이터베이스로 전송됩니다.
    다시 말하지만, 관리되는 스레드는 데이터베이스에서 쿼리를 처리하는 동안 다른 작업을 자유롭게 수행할 수 있습니다. 다른 모든 실행이 완료되었으므로 스레드는 대기 호출에서 중지됩니다.
  5. 쿼리 반환 및 결과가 콘솔에 기록됩니다.

비동기 출력  

 

핵심요약

이제 EF의 비동기 메서드를 사용하는 것이 얼마나 쉬운지 확인했습니다. 간단한 콘솔 앱에서는 비동기의 장점이 그리 명백하지 않을 수 있지만, 장기 실행 또는 네트워크 바인딩된 활동이 애플리케이션을 차단하거나 많은 스레드가 메모리 공간을 늘릴 수 있는 상황에서도 이와 동일한 전략을 적용할 수 있습니다.