1

まず第一に、私は愚かなことを尋ねているかどうかわからないので、始める前に謝罪してください.

SqlCommand を非同期的に使用してエンティティのコレクションを DB に保存するメソッドがあり、非同期操作を表す Task を返しますが、これまでのところうまくいきます。基本的に、エンティティごとに SQL コマンドと一連のパラメーターを生成し、それらすべてを SqlCommand インスタンスに追加します (パラメーターには番号が付けられています)。

しかし、多くのエンティティを挿入しようとすると、パラメーター数が 2100 に達すると、SQL 操作が失敗します。これが制限であるためです。次に、エンティティ コレクションをバッチで分割し、それらを順番に実行してから、すべての子タスクが完了するまで実行されないタスクを返します。(たぶん最後)

各子タスクは、変更された行数を示す Int32 を取得し、最終的なタスクはそれらすべての合計を返す必要があります。したがって、すべてのタスクはタスクです。1 つが失敗した場合、すべてを「ロールバック」する必要があるため、接続オブジェクトとトランザクション オブジェクトを共有する必要があります。

また、Task および SqlCommand で I/O 完了ポートを正しく使用していること、および SQL Server での操作が行われている間、スレッドが待機/スピンしていないことを確認したいと思います。この「保存」操作はASP.NET MVC の非同期コントローラーから呼び出されます。

ここで正しいアプローチは何でしょうか?

よろしく。

4

3 に答える 3

1

挿入を分割して順番に実行する場合は、呼び出しごとにタスクを使用する必要はありません。通常の順次コードが必要です。

Task を開始してそれを返すメソッドを作成します。タスク本体では、必要な通常の順次コードを実行するだけです。終了したら、タスク本体を終了すると、単一のタスクが完了します。

于 2012-04-20T14:20:56.493 に答える
1

C# 5 (およびタスクベースの非同期を想定) では、これは非常に簡単です。

async Task<IReadOnlyList<SomeType>> PerformUpdatesAsync(
    IEnumerable<AnotherType> data)
{
    var result = new List<SomeType>();

    foreach (var batch in data.SplitIntoBatches(BatchSize))
        result.Add(await PerformUpdateAsync(batch));

    return result;
}

C# 5 より前のバージョンで非同期foreachをシミュレートするのはより困難ですが、たとえば、処理するバッチのキューと「再帰的」ラムダを使用して実行できます。

Task<SomeType[]> PerformUpdatesAsync(IEnumerable<AnotherType> data)
{
    var batches = new Queue<IEnumerable<AnotherType>>(
        data.SplitIntoBatches(BatchSize));

    var result = new List<SomeType>();

    var tcs = new TaskCompletionSource<SomeType[]>();

    AsyncCallback onEndUpdate = null;
    onEndUpdate =
        ar =>
        {
            result.Add(EndUpdate(ar));

            if (batches.Count == 0)
                tcs.SetResult(result.ToArray());
            else
                BeginUpdate(batches.Dequeue(), onEndUpdate, null);
        };

    BeginUpdate(batches.Dequeue(), onEndUpdate, null);

    return tcs.Task;
}

この方法でクロージャーを使用したくない場合は、フィールドにすべてのローカル変数を保持する別のクラスを作成することで、同じことを行うことができます。

フレームワークには存在SplitIntoBatches()しないため、自分で作成する必要があります (まだ作成していない場合)。

于 2012-04-20T16:55:43.220 に答える
0

これは良い質問です。ここにいくつかのコードがあります:

Task<int>[] batchTasks = ...;//start the batches here. each task should return the count of changed rows
Task<int> sumTask = Task.Factory.ContinueWhenAll(batchTasks, _ => {
 return batchTasks.Sum(x => x.Result);
});

これにより、個々のバッチのブロッキングのない合計が得られます。

バッチを開始するときは、必ず BeginExecuteReader などの非同期 SQL API を使用してください。その後、完全にブロックされなくなります。

于 2012-04-20T12:30:45.297 に答える