ASP.NET は本質的にマルチスレッド システムであることに注意してください。DataView
読み取りはスレッドセーフであると文書化されていますが、書き込みはそうではありません。これを考慮することが重要です。
DataTable
提示されたスニペットでは、キャッシュからa のインスタンスをプルし、DefaultView
フィルタリングして新しいものに書き込むためにそのインスタンスにアクセスするDataTable
と、安全でないことが証明される状態が発生する可能性があります。これは、フィルター内の列が原因ではなく、複数の同時に同じコードを実行できるスレッド。
実装の詳細として、DataView
内部状態のいくつかのビットを利用します。実行中、ToTable()
複数の非ローカル状態が関係します。特に、DataRow をキーとするディクショナリ フィールドと、キーとして使用される DataRow フィールドがあります。このディクショナリはクリアされ、追加され、行を参照する値で行が上書きされ、次に null に設定されます。これはすべてプロセスの一部です。複数のスレッドが同時にこれを実行している場合、あるスレッドが別のスレッドが依存している状態を上書きして無効にすることは考えられません。これは、質問に記載されている例外や、その他の潜在的に有害な結果につながる可能性があります。
とにかく、複数の実行を利用できる環境でも、コード スニペットを出発点として使用して問題を再現してみましょう。
static void Main()
{
var table = new DataTable();
table.Columns.Add("Foo");
table.Columns.Add("ID", typeof(int));
for (int i = 0; i < 100; i++)
{
table.Rows.Add(i.ToString(), i);
}
for (int j = 0; j < 100; j++)
{
Enumerable
.Range(0,100)
.AsParallel()
.ForAll(item => ExecuteToTable(table, item));
}
}
static void ExecuteToTable(DataTable table, int item)
{
var view = table.DefaultView;
view.RowFilter = string.Format("Foo = '{0}'", item);
var filteredTable = view.ToTable();
}
これは例外を生成しますか? 実行して確認してください。複数回の実行が必要になる場合がありますが、コードを実行しているマシンが私のものと似ていれば、それほど多くはかかりません。(並列クエリのループでは、実際には 1 回だけかかります。)
このコードを LinqPad で実行したところ、「望ましい」例外が生成されました。これは並列クエリ実行であるため、AggregatedException にラップされますが、InnerException が物語を語ります。
KeyNotFoundException: 指定されたキーがディクショナリに存在しませんでした。
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at System.Data.DataView.CopyTo(DataRowView[] array, Int32 index)
at System.Data.DataView.GetEnumerator()
at System.Data.DataView.ToTable(String tableName, Boolean distinct, String[] columnNames)
at System.Data.DataView.ToTable()
それで、私たちはそれを再現し、うまくいけばそれを理解しました. 避けてみてはどうですか?可能な解決策はいくつかありますが、ユースケースによっては、他のものよりも口当たりの良いものもあり、大規模な書き直しからわずかな変更までさまざまです。
あなたはできる
DefaultView にアクセスするtable.Copy()
前に、DataTableを使用してコピーします。これにより、各リクエストに独自のテーブルが与えられます (上記のスニペット)。ただし、テーブルが大きい場合、コピーのコストが高くなる可能性があります。を使用して上記の再現コードを実行しCopy()
、例外が回避されるかどうかを確認してください。
DataView を完全に避けてください。Linq は、DataTable に対しても役立ちます。以下のスニペットを使用して、フィルター処理された DataTable 出力を生成できます。ただし、フィルターを通過する行がない場合は、独自のCopyToDataTable()
例外がスローされる可能性があることにも注意してください。その可能性がある場合は、コードを分割し、最後の部分を呼び出す前に (.Any() を使用して) 結果を確認してください。に対するもう 1 つの欠点は、利用可能なオーバーロードを利用する場合、出力テーブルに含める列をa を使用して指定できることです。DataView
DataView
ToTable
var filteredTable
= table.AsEnumerable()
.Where(row => string.Equals(row.Field<string>("Foo"), item.ToString(), StringComparison.InvariantCultureIgnoreCase))
.CopyToDataTable();
もちろん、コードをさらに再設計したり、スレッド セーフのための独自の手段をキャッシュ戦略に追加したりすることもできますが、実際のテーブルがフィルター処理される方法を変更する実装には時間がかかります。