次の方法で共有


非同期クエリと保存

EF6 以降のみ - このページで説明されている機能、API などが Entity Framework 6 で導入されました。 以前のバージョンを使用している場合、一部またはすべての情報は適用されません。

EF6 では、非同期クエリのサポートが導入され、.NET 4.5 で導入された async キーワードと await キーワード を使用して保存しました。 すべてのアプリケーションが非同期のメリットを得られるわけではありませんが、実行時間の長いタスク、ネットワークタスク、または 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 の進行状況を監視するタスク (12 行目) としてキャプチャし、プログラムのすべての作業が完了すると完了するまでこのタスクのプログラム実行をブロックしています (18 行目)。
  4. 25 行目: PerformSomeDatabaseOperationsasync としてマークされ、 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. すべての ブログの クエリがデータベースに送信される
    ここでも、データベースでクエリが処理されている間、マネージド スレッドは他の作業を自由に実行できます。 他のすべての実行が完了したので、スレッドは Wait 呼び出しで停止するだけです。
  5. クエリの戻り値と結果がコンソールに書き込まれる

非同期出力  

 

持ち帰り

EF の非同期メソッドを使用するのがいかに簡単であるかを確認しました。 非同期の利点は単純なコンソール アプリではあまり明らかでない場合がありますが、実行時間の長いアクティビティやネットワーク バインドアクティビティによってアプリケーションがブロックされたり、多数のスレッドがメモリ占有領域を増やしたりする場合にも、これらの同じ戦略を適用できます。