【问题标题】:How to map a DataReader to class properties and maintain performance?如何将 DataReader 映射到类属性并保持性能?
【发布时间】:2011-09-02 19:32:49
【问题描述】:

序言:

  1. 所有数据连接字符串, 连接等是使用创建的 DbProviderFactories。
  2. 代码混合了 C# 和 VB.Net 来自 多个库。

我正在将 DbDataReader 映射到实体并有一些基准:

[0] retrieved 159180 records in 45135 ms
[1] retrieved 159180 records in 45008 ms
[2] retrieved 159180 records in 44814 ms
[3] retrieved 159180 records in 44987 ms
[4] retrieved 159180 records in 44914 ms
[5] retrieved 159180 records in 45224 ms
[6] retrieved 159180 records in 45829 ms
[7] retrieved 159180 records in 60762 ms
[8] retrieved 159180 records in 52128 ms
[9] retrieved 159180 records in 47982 ms  

考虑到从 Sql Server Management Studio 查询只需要 17 秒,这是一个相当长的时间并且非常糟糕。我的选择语句:

"SELECT * FROM tbl_MyTable"

表包含 43 个字段,并且可能没有按应有的最佳方式编制索引;但是,执行全选,我不认为索引会有问题。所以......这就是我正在做的事情:

定义一个实体:

public class Concept
{
    #region Columns
    [DataParameter("ConceptID", DbType.Int32)]
    public Int32 ConceptID
    { get; set; }
    [DataParameter("ConceptName", DbType.String)]
    public string ConceptName
    { get; set; }
    [DataParameter("ConceptTypeID", DbType.Int32)]
    public Int32 ConceptTypeID
    { get; set; }
    [DataParameter("ActiveYN", DbType.Boolean)]
    public bool ActiveYN
    { get; set; }
    #endregion
}

查询数据读取器:

for (int i = 0; i <= 99; i++)
{
    sw.Start();
    var results = session.QueryReader<Concept>(
        new SqlCommand(command), dr => new Concept());

    sw.Stop();

    Console.WriteLine("[{0}] retrieved {1} records in {2} ms", i, results.Count(), sw.ElapsedMilliseconds);
    sw.Reset();
}

...调用:

Public Function QueryReader(Of TEntity As {Class, New})(ByVal Command As DbCommand, _
                                                        ByVal Projection As Func(Of DbDataReader, TEntity)) _
                                                        As IEnumerable(Of TEntity)

    Dim list As IEnumerable(Of TEntity)

    Command.Connection = dataReader.NewConnection
    Command.Connection.Open()

    Using _reader As DbDataReader = Command.ExecuteReader()
        list = _reader.Query(Of TEntity)(Projection).ToList()
    End Using

    Command.Connection.Close()

    Return list
End Function

... 和扩展方法QueryReader&lt;T&gt;编辑新 TEntity() 的位置 - 感谢@Henk

public static IEnumerable<TEntity> Query<TEntity>(this DbDataReader Reader,
    Func<DbDataReader, TEntity> Projection)
    where TEntity : class, new()
{
    //   moving this reflection to another class
    Dictionary<string, PropertyInfo> props;

    while (Reader.Read())
    {
        TEntity entity = new TEntity();

        if (!entities.TryGetValue(typeof(TEntity).ToString(), out props))
        {
            //  reflection over TEntity
            props = (from p in entity.GetType().GetProperties()
                     from a in p.GetCustomAttributes(typeof(DataParameterAttribute), false)
                     select p)
                     .ToDictionary(p => p.Name);

            entities.Add(typeof(TEntity).ToString(), props);
        }

        foreach (KeyValuePair<string, PropertyInfo> field in props)
        {
            if (null != Reader[field.Key] && Reader[field.Key] != DBNull.Value)
            { field.Value.SetValue(entity, Reader[field.Key], null); }
        }

        yield return entity;
    }
}

任何关于提高性能的建议将不胜感激......


更新

我按照@EtienneT 的建议实施了 dapper-dot-net - 以下是检索时间:

[0] retrieved 159180 records in 6874 ms
[1] retrieved 159180 records in 6866 ms
[2] retrieved 159180 records in 6570 ms
[3] retrieved 159180 records in 6785 ms
[4] retrieved 159180 records in 6693 ms
[5] retrieved 159180 records in 6735 ms
[6] retrieved 159180 records in 6627 ms
[7] retrieved 159180 records in 6739 ms
[8] retrieved 159180 records in 6569 ms
[9] retrieved 159180 records in 6666 ms

【问题讨论】:

  • 160K 记录,43 个字段通过反射填充实体?我会说 45-50 秒相当不错,但也许我太宽容了。
  • @Anthony:我认为它体面 ...但我想要 - 特别是因为我使用字典来映射读者[字段]到propertyInfo ...但也许我的期望太高了。 ;)
  • 只是为了好玩,尝试编写一个老式的while(Reader.Read()) 循环,Fx 2.0 风格。它应该让你知道所有这些反射和间接的代价是多么的大。
  • 混合饮料? (c# 和 vb)
  • @AranMulholland:不幸的是,是的 :) 当时我有一个由另一个开发人员用 VB 编写的 DAL,我正在用 C# 构建后端。 .Net 之美!

标签: c# reflection datareader


【解决方案1】:

您是否考虑过像 dapper.net 这样的微型 ORM?

https://github.com/StackExchange/dapper-dot-net

它由 StackOverflow 的开发人员制作,并将 SQL 查询直接映射到您的对象。它生成并缓存 IL 代码以将 SQL 结果映射到您的对象。因此,每种类型只生成一次 IL 代码。从来没有用过这个,但是如果你需要性能来将你的 SQL 结果映射到 .net 对象,它就是你需要的库。

【讨论】:

  • 哇,不知道它的存在。看起来很漂亮
  • 我现在正在查看它 - 我想我可以给它一个非常快速的测试 - 我会在完成后立即发布一些基准测试。
  • Rob Conery 制作的类似东西:blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive 也很快,但它更多地处理动态对象。从我阅读的内容来看,Dapper 仍然更快。
  • 我看到的关于 Dapper 的唯一负面影响是它生成 IL 来映射您的对象,然后缓存它。因此,如果生成的 IL 中存在错误,祝你好运。看dapper代码:github.com/SamSaffron/dapper-dot-net/blob/master/Dapper/…
  • 到目前为止,它似乎很快而且没有问题。我不认为 Marc Gravell 会涉及 IL 错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-12
  • 1970-01-01
  • 2019-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多