巨大的列表性能和内存消耗

时间:2012-05-28 17:41:31

标签: c# .net performance list ado.net

我正在开始一个新的应用程序。我运行查询到数据库然后我返回一个海关对象列表。这是一个例子:

使用此方法,我可以获得表格的所有记录。示例: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();
    }
}

简而言之,我们的想法是迭代一个SqlDataReader(带有yield return)并创建一个实体,最后将它存储在List上。

我认为这会耗费大量资源。该查询返回aprox 22k记录,并且将经常调用GetAll方法。有时会每隔1或2分钟调用一次GetAll方法。

我只执行了几次(10次aprox)GetAll方法,Windows任务管理器上的信息显示内存在几秒钟内从17MB增长到45 MB。

我相信这段代码不是表现。

问题是:如何让代码消耗更少的内存?可能是我必须更改列表,但有什么替代方案?

2 个答案:

答案 0 :(得分: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);
    }
}

这将消耗更少的内存,但它也将保持与SQL Server的连接打开。 请在this MSDN article上阅读更多内容。

答案 1 :(得分:1)

另一个选择可能是委托每个项目。 这更明确,您可以更好地控制每个项目。 但是你需要小心,委托调用将阻止迭代。 我更喜欢使用IEnumerable,因为它在.NET中有很多功能(PLinq,foreach,Collection-ctors)。

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));
}