データテーブルを汎用リストに変換する最速の方法
-
22-07-2019 - |
質問
データテーブルを返すデータ層選択メソッドがあります。これは、強く型付けされたジェネリックリストを返すビジネス層メソッドから呼び出されます。
私がやりたいことは、この質問とよく似ています(ただし、同じではありません):
DataTableを一般的なリストに変換するにはどうしますか?
違いは、リストにデータ行ではなく厳密に型指定されたオブジェクトを含めることです(また、ここにはまだlinqがありません)。
パフォーマンスが心配です。ビジネス層メソッドは、プレゼンテーション層から順番に呼び出され、結果はユーザーに表示するために繰り返されます。ビジネス層で追加のイテレーションを追加するのは非常に無駄に思えますが、プレゼンテーションのためにすぐにもう一度行うだけなので、これをできるだけ早くしたいです。
これは一般的なタスクであるため、繰り返し繰り返すことができる良いパターンを本当に探しています。
解決
DataTable
の構造とタイプされたオブジェクトを事前に知っていますか?マッピングを行うためにデリゲートを使用できます。そうしない場合(つまり、Type
とプロパティのみ)、動的なメンバーアクセスを加速する方法があります(HyperDescriptor
など)。
どちらの方法でも、イテレータブロックを検討してください。そうすれば、オブジェクトを2回バッファリングする必要がなくなります。もちろん、小さい行数のみを処理している場合、これは問題ではありません。
これらのポイントを明確にできますか?さらに詳細を追加できます...
最も単純な場合、何が問題なのですか:
DataTable table = new DataTable {
Columns = {
{"Foo", typeof(int)},
{"Bar", typeof(string)}
}
};
for (int i = 0; i < 5000; i++) {
table.Rows.Add(i, "Row " + i);
}
List<MyType> data = new List<MyType>(table.Rows.Count);
foreach (DataRow row in table.Rows) {
data.Add(new MyType((int)row[0], (string)row[1]));
}
(上記の問題は正しいアプローチを導くかもしれません...)
他のヒント
上記のサンプルの問題は、非常に遅いことです。約400行のDataTableがあり、この変換には5〜6秒かかります。
これは非常に一般的なタスクのように見えるので、ここに、よりパフォーマンスの高いソリューションを持っている人がいないことに驚いています。
*アップデート!! * キックのために、DataTableを反復処理してリストに追加する代わりに、LINQを使用して変換しようと考えました。以下は私がしたことです:
List<MyObject> returnList = new List<MyObject>();
MyDataTable dtMyData = new MyTableAdapter().GetMyData();
returnLists = (from l in dtMyData
select new MyObject
{
Active = l.IsActive,
Email = l.Email,
//...
//About 40 more properties
//...
ZipCode = l.Zipcode
}).ToList();
最初の方法(各行を繰り返す)には5.3秒かかり、LINQを使用する方法には1.8秒かかりました!
とにかくデータ行を繰り返し処理してオブジェクトに変換する必要がありますが、データ行のコレクションを取得し、代わりに要求時に各項目をオブジェクトタイプに変換するコンストラクターを使用してカスタムコレクションを作成できます一度に。
IList<T>
を実装するカスタムコレクションを定義するとします。その後、2つの内部フィールドを持つことができます-データ行のコレクションへの参照とList<T>
。 <=>はデータ行のコレクションの長さに初期化されますが、null値が含まれます。
インデクサーで、<=>にそのインデックスの値が含まれているかどうかを確認できます。そうでない場合は、有用な手段を使用してオブジェクトを作成し、返す前にそこに保存できます。
これにより、オブジェクトの作成は要求されるまで延期され、要求されたオブジェクトのみを作成します。
オブジェクトにはおそらくDataRowを取得するコンストラクタが必要になるか、オブジェクトを作成するために何らかのファクトリが必要になりますが、それは別のトピックです。
より多くのユーザーに提供するために-シンプルなコンソールアプリでのMarkの回答への親しみやすさ:
class Program
{
static void Main(string[] args)
{
//define a DataTable obj
DataTable table = new DataTable
{
Columns = {
{"Foo", typeof(int)},
{"Bar", typeof(string)}
}
};
//populate it the DataTable
for (int i = 0; i < 3; i++)
{
table.Rows.Add(i, "Row " + i);
}
List<MyType> listWithTypedObjects= new List<MyType>(table.Rows.Count);
foreach (DataRow row in table.Rows)
{
listWithTypedObjects.Add(new MyType((int)row[0], (string)row[1]));
}
Console.WriteLine(" PRINTING THE POPULATED LIST ");
foreach (MyType objMyType in listWithTypedObjects)
{
Console.Write(" I have object of the type " + objMyType.ToString() + " => " );
Console.Write(" with Prop1OfTypeInt " + objMyType.Prop1OfTypeInt.ToString() + " , ");
Console.WriteLine(" with Prop1OfTypeInt " + objMyType.Prop2OfTypeString.ToString() + " ");
}
Console.WriteLine(" \n \n \n HIT A KEY TO EXIT THE PROGRAM ");
Console.ReadKey();
}
}
class MyType {
public int Prop1OfTypeInt { get; set; }
public string Prop2OfTypeString { get; set; }
/// <summary>
/// Note the order of the passed parameters is important !!!
/// </summary>
public MyType( int prop1OfTypeInt , string prop2OfTypeString)
{
this.Prop1OfTypeInt = prop1OfTypeInt;
this.Prop2OfTypeString = prop2OfTypeString;
}
}
public partial class issuereceive_manageroffice_bal
{
public int issue_id{get;set;}
public string process{get;set;}
public DateTime issue_date{get;set;}
public TimeSpan issue_time{get;set;}
public string eg_no{get;set;}
public string lotno{get;set;}
public string clarity{get;set;}
public string sieves{get;set;}
public string shape{get;set;}
public double issue_carat{get;set;}
public int issue_pieces{get;set;}
public int receive_pieces{get;set;}
public double receive_carat{get;set;}
public int kp_pieces{get;set;}
public decimal kp_carat{get;set;}
public double loss{get;set;}
public string issue_manager{get;set;}
public string issue_by{get;set;}
public string receive_by{get;set;}
public int status{get;set;}
public DateTime receive_date{get;set;}
public string receive_time{get;set;}
public int factory_id{get;set;}
}
List<issuereceive_manageroffice_bal> issue_receive_list = new List<issuereceive_manageroffice_bal>();
issue_receive_list =
(from DataRow dr in DataTable.Rows
select new issuereceive_manageroffice_bal()
{
issue_id = 0,
issue_time = TimeSpan.Parse("0"),
receive_time = null,
shape = null,
process = dr["process"].ToString(),
issue_date = Convert.ToDateTime(dr["issue_date"]),
eg_no = dr["eg_no"].ToString(),
lotno = dr["lotno"].ToString(),
clarity = dr["clarity"].ToString(),
sieves = dr["sieves"].ToString(),
issue_carat = dr["issue_carat"].ToString() != "" ? double.Parse(dr["issue_carat"].ToString()) : 0,
issue_pieces = dr["issue_pieces"].ToString() != "" ? int.Parse(dr["issue_pieces"].ToString()) : 0,
receive_carat = dr["receive_carat"].ToString() != "" ? double.Parse(dr["receive_carat"].ToString()) : 0,
kp_pieces = dr["kp_pieces"].ToString() != "" ? int.Parse(dr["kp_pieces"].ToString()) : 0,
kp_carat = dr["kp_carat"].ToString() != "" ? decimal.Parse(dr["kp_carat"].ToString()) : 0,
loss = dr["loss"].ToString() != "" ? double.Parse(dr["loss"].ToString()) : 0,
issue_manager = dr["lotno"].ToString(),
issue_by = dr["issue_by"].ToString(),
receive_by = dr["receive_by"].ToString(),
status = dr["status"].ToString() != "" ? int.Parse(dr["status"].ToString()) : 0,
receive_date = Convert.ToDateTime(dr["receive_date"]),
factory_id = dr["factory_id"].ToString() != "" ? int.Parse(dr["factory_id"].ToString()) : 0,
}).ToList();