1. ホーム
  2. c#

[解決済み] Entity Framework SaveChanges() と SaveChangesAsync() の比較と Find() と FindAsync() の比較

2022-11-26 22:44:57

質問

上記の2つのペアの違いについて調べているのですが、明確に説明している記事がなく、また、どのような場合にどちらを使用するのかがわかりません。

では SaveChanges()SaveChangesAsync() ?

そして、その間に Find()FindAsync() ?

サーバ側では Async メソッドを使用する場合にも await . 従って、サーバー側では非同期ではないと思います。

クライアント側のブラウザでUIブロックを防ぐのに役立つだけなのでしょうか?あるいは、両者の間に長所と短所があるのでしょうか?

どのように解決するのですか?

リモートサーバー上でアクションを実行する必要があるときはいつでも、あなたのプログラムはリクエストを生成し、それを送信し、そして応答を待ちます。ここでは SaveChanges()SaveChangesAsync() を例として挙げましたが、同じように Find()FindAsync() .

例えば、リスト myList というリストがあり、それをデータベースに追加する必要があるとします。それを挿入するために、あなたの関数は次のようなものになるでしょう。

using(var context = new MyEDM())
{
    context.MyTable.AddRange(myList);
    context.SaveChanges();
}

まず MyEDM を作成し、リスト myList をテーブル MyTable を呼び出した後 SaveChanges() を呼び出して変更をデータベースに永続化します。レコードはコミットされますが、プログラムはコミットが終了するまで他のことは何もできません。これは、コミットする内容によっては長い時間がかかることがあります。レコードへの変更をコミットする場合、エンティティはそれらを一度にコミットする必要があります (私はかつて、保存が更新のために 2 分かかったことがあります)!

この問題を解決するには、2 つの方法のうちの 1 つを実行します。1 つ目は、挿入を処理するために新しいスレッドを立ち上げることです。これは、実行を続けるために呼び出し側のスレッドを解放しますが、ただそこに座って待つだけの新しいスレッドを作成したことになります。そのようなオーバーヘッドは必要ありません。 async await パターンが解決してくれます。

I/Oの対向のために await はすぐにあなたの親友になります。上記のコードセクションを利用して、次のように修正することができます。

using(var context = new MyEDM())
{
    Console.WriteLine("Save Starting");
    context.MyTable.AddRange(myList);
    await context.SaveChangesAsync();
    Console.WriteLine("Save Complete");
}

とても小さな変更ですが、コードの効率とパフォーマンスに大きな効果があります。では、どうなるのでしょうか?コードの始まりは同じです。 MyEDM のインスタンスを作成し、そこに myListMyTable . しかし await context.SaveChangesAsync() を呼び出すと、コードの実行は は呼び出し元の関数に戻ります! つまり、すべてのレコードがコミットされるのを待っている間、あなたのコードは実行を続けることができるのです。上記のコードを含む関数が次のようなシグネチャを持っていたとします。 public async Task SaveRecords(List<MyTable> saveList) というシグネチャを持っていたとすると、呼び出し側の関数は次のようになります。

public async Task MyCallingFunction()
{
    Console.WriteLine("Function Starting");
    Task saveTask = SaveRecords(GenerateNewRecords());

    for(int i = 0; i < 1000; i++){
        Console.WriteLine("Continuing to execute!");
    }

    await saveTask;
    Console.Log("Function Complete");
}

なぜこのような関数があるのか、私にはわかりませんが、この関数が出力するものは、どのように async await がどのように機能するかを示しています。まず、何が起こるかを確認しましょう。

実行が入る MyCallingFunction , Function Starting では Save Starting がコンソールに書き込まれ、その後、関数 SaveChangesAsync() が呼び出される。この時点で、実行は MyCallingFunction に戻り、「実行の継続」を1000回まで書き続けるforループに入る。このとき SaveChangesAsync() が終了すると、実行は SaveRecords 関数に戻り Save Complete をコンソールに書き出します。一度、すべての SaveRecords が完了すると、実行は MyCallingFunction で実行を継続します。 SaveChangesAsync() が終了しました。混乱しましたか?以下は出力例です。

関数開始
保存 開始
実行継続中
実行継続
実行継続
実行の継続!
実行中!
....
実行の継続!
保存完了!
実行の続き!
実行の続き!
実行継続中
....
実行継続中!
機能完了

あるいは、そうかもしれません。

起動時の機能
起動を保存
実行継続中
実行継続
保存完了
実行継続!
実行中
実行の続き!
....
実行継続中!
機能完了

の素晴らしさです。 async await とすることで、何かの終了を待っている間、コードを実行し続けることができます。実際には、このような関数を呼び出し関数として持つことになります。

public async Task MyCallingFunction()
{
    List<Task> myTasks = new List<Task>();
    myTasks.Add(SaveRecords(GenerateNewRecords()));
    myTasks.Add(SaveRecords2(GenerateNewRecords2()));
    myTasks.Add(SaveRecords3(GenerateNewRecords3()));
    myTasks.Add(SaveRecords4(GenerateNewRecords4()));

    await Task.WhenAll(myTasks.ToArray());
}

ここでは、4種類の保存記録関数があります。 が同時に . MyCallingFunction を使うと、より速く完了します。 async await を使うと、個々の SaveRecords 関数が直列に呼ばれる場合よりも

まだ触れていないのが、この await キーワードです。これは現在の関数の実行を停止させるものです。 Task が完了するまで現在の関数を停止させます。つまり、オリジナルの MyCallingFunction という行は Function Complete の行がコンソールに書き込まれるのは SaveRecords 関数が終了するまでコンソールには書き込まれません。

長い話になりますが、もしオプションで async await を使用するオプションがある場合は、アプリケーションのパフォーマンスを大幅に向上させることができるため、使用する必要があります。