0

新しいアプリケーションを開始しています。データベースに対してクエリを実行してから、税関オブジェクトのリストを返します。次に例を示します。

このメソッドを使用して、テーブルのすべてのレコードを取得します。例:SELECT ID、NAME、LASTNAME FROM PKR_PLAYER

public List<TEntity> GetAll()
{
    List<TEntity> all = new List<TEntity>();
    String query = String.Format("SELECT {0} FROM {1}",AllFieldsSelection,TableName);
    var data = SqlExecutionData.Create().WithConn(ConnectionString).WithQuery(query);
    foreach (IDataRecord record in SqlServerUtils.GetRecords(data))
    {
        all.Add(CreateOneFromRecord(record));
    }
    return all;
}

これを使用して、レコードに保存されているデータを使用してTEntityObjectを作成します

private TEntity CreateOneFromRecord(IDataRecord record)
{
    var result = new TEntity();
    for (int i = 0; i < record.FieldCount; i++)
    {
        ColumnMap colMap = maps.FirstOrDefault(x => x.Column.Equals(record.GetName(i)));
        if (colMap == null || record.IsDBNull(i)) continue;
        object value = record.GetValue(i);
        colMap.PropertyInfo.SetValue(result, value, null);
    }
    return result;
}

クエリを実行する方法

public static IEnumerable<IDataRecord> GetRecords(SqlExecutionData data)
{
    using (SqlConnection sqlConnection = new SqlConnection(data.ConnectionString))
    using (SqlCommand command = new SqlCommand(data.Query, sqlConnection))
    {
        if (data.Parameters!=null && data.Parameters.Count > 0)
        {
            foreach (String key in data.Parameters.Keys)
                command.Parameters.AddWithValue(key, data.Parameters[key]);
        }
        sqlConnection.Open();

        using (IDataReader rdr = command.ExecuteReader())
        {
            while (rdr.Read())
            {
                yield return (IDataRecord)rdr;
            }
        }

        sqlConnection.Close();
    }
}

簡単に言うと、(yield returnを使用して)SqlDataReaderを反復処理し、エンティティを作成して、最終的にリストに保存するという考え方です。

これは多くのリソースを消費すると思います。クエリは約22kレコードを返し、メソッドGetAllは非常に頻繁に呼び出されます。GetAllメソッドが1分または2分ごとに呼び出される場合があります。

GetAllメソッドを数回(約10回)実行すると、Windowsのタスクマネージャーの情報から、メモリが数秒で17MBから45MBに増加することがわかります。

このコードはパフォーマンスではないと思います。

問題は、コードがより少ないメモリを消費するようにするにはどうすればよいですか?リストを変更する必要があるかもしれませんが、代替手段は何ですか?

4

2 に答える 2

2

使用することもできますyield returnが、メソッドを少し変更する必要があります。
これにより、メモリ消費が最小限に抑えられます。

public IEnumerable<TEntity> GetAll()
{
    String query = String.Format("SELECT {0} FROM {1}",AllFieldsSelection,TableName);
    var data = SqlExecutionData.Create().WithConn(ConnectionString).WithQuery(query);
    foreach (IDataRecord record in SqlServerUtils.GetRecords(data))
    {
        yield return CreateOneFromRecord(record);
    }
}

これにより、消費されるメモリははるかに少なくなりますが、SQLServerへの接続も開いたままになります。このMSDNの記事で詳細をお読みください。

于 2012-05-28T17:46:49.423 に答える
1

別のオプションは、各アイテムを委任することです。これはより明確であり、各アイテムをより細かく制御できます。ただし、注意する必要があります。デリゲート呼び出しは反復をブロックします。IEnumerableは、.NETに非常に多くの機能(PLinq、foreach、Collection-ctors)があるため、私はIEnumerableを使用することを好みます。

public void IterateItems(Action<int, TEntity> handler)
{
    String query = String.Format("SELECT {0} FROM {1}",AllFieldsSelection,TableName);
    var data = SqlExecutionData.Create().WithConn(ConnectionString).WithQuery(query);

    var index = 0;   
    foreach (IDataRecord record in SqlServerUtils.GetRecords(data))
    {
        handler(CreateOneFromRecord(index, record));
        index++;
    }
}

メモリ効率の高い呼び出し:

public void MemoryEfficient()
{
    ProcessItems((index, item) => Console.WriteLine("{0}: {1}", index, item));
}

リスト内のアイテムを収集します。

public void FillAList()
{
    var list = new List<TEntity>();
    ProcessItems((index, item) => list.Add(item));
}
于 2012-05-29T08:14:56.487 に答える