代码之家  ›  专栏  ›  技术社区  ›  David Neale

数据阅读器-硬编码序列?

  •  15
  • David Neale  · 技术社区  · 14 年前

    当从返回数据时, DataReader 我通常在 数据存储器 要获取相关列:

    if (dr.HasRows)         
       Console.WriteLine(dr[0].ToString());
    

    if (dr.HasRows)         
       Console.WriteLine(dr.GetString(0));
    

    if (dr.HasRows)         
       Console.WriteLine((string)dr[0]);
    

    我一直这样做,因为我在早期被建议使用 dr["ColumnName"] 或者更优雅的索引方式会导致性能下降。

    然而,尽管所有对数据实体的引用都变得越来越强类型化,但我对此感到更不舒服。我也知道上面没有检查 DBNull .

    数据存储器 ?

    6 回复  |  直到 7 年前
        1
  •  24
  •   Mark Wilkins    14 年前

    在这种情况下,双方都有可能争论。正如其他人已经指出的那样,使用这个名称更易读,并且如果有人更改了基础数据库中列的顺序,它将不会中断。但也有人可能会争辩说,如果有人更改了基础数据库中的列名,那么使用序数有不破坏的优势。不过,我更喜欢前一个参数,并且认为列名的可读性参数通常优于第二个参数。另外一个关于名字的论点是,它可以__自我检测_错误。如果有人确实更改了字段名,那么代码更有可能被破坏,而不是在读取错误字段时出现细微的错误。

    这看起来很明显,但也许值得一提的是,一个既有自我检测错误又有序数性能的用例。如果您在SQL中显式地指定了选择列表,那么使用序数就不会有问题,因为代码中的语句保证了顺序:

    SELECT name, address, phone from mytable
    

    在这种情况下,使用序数访问数据是相当安全的。如果有人在表中移动字段,这并不重要。如果有人更改了名称,那么SQL语句在运行时会产生一个错误。

    最后一点。我刚刚在我帮助编写的提供者上运行了一个测试。测试读取100万行并访问每个记录上的__lastname_157;字段(与值进行比较)。用法 rdr[“lastname”] 处理时间为3301毫秒,而 rdr.GetString(1) 用了2640毫秒(大约加速25%)。在这个特定的提供程序中,名称的查找使用排序查找将名称转换为序号。

        2
  •  15
  •   Josh    14 年前

    字符串名称查找比序数调用要昂贵得多,但比硬编码序数更容易维护,也不容易“脆弱”。我就是这么做的。这是两个世界中最好的。我不必记住序数值,也不必关心列顺序是否发生变化,但我可以从使用序数中获得性能优势。

    var dr = command.ExecuteQuery();
    if (dr.HasRows)
    {
        //Get your ordinals here, before you run through the reader
        int ordinalColumn1 = dr.GetOrdinal("Column1");
        int ordinalColumn2 = dr.GetOrdinal("Column2");
        int ordinalColumn3 = dr.GetOrdinal("Column3");
    
        while(dr.Read())
        {
            // now access your columns by ordinal inside the Read loop. 
            //This is faster than doing a string column name lookup every time.
            Console.WriteLine("Column1 = " + dr.GetString(ordinalColumn1);
            Console.WriteLine("Column2 = " + dr.GetString(ordinalColumn2);
            Console.WriteLine("Column3 = " + dr.GetString(ordinalColumn3);
        }
    }
    

    注意:这只对您希望在其中有相当数量的行的读者真正有意义。这个 GetOrdinal() 打电话是额外的,只有当你从打电话中获得的综合存款 GetString(int ordinalNumber) 在循环中大于调用成本 GetOrdinal .

    编辑:错过了这个问题的第二部分。关于dbnull值,我已经开始编写处理这种可能性的扩展方法。例子: dr.GetDatetimeSafely() 在这些扩展方法中,您可以做任何您需要做的事情来确信您得到了预期的值。

        3
  •  4
  •   Tejs    14 年前

    我总是使用字符串名称方法,因为读取代码更干净。必须从精神上分析索引到列名是可怕的。

        4
  •  3
  •   Brian Gideon    14 年前

    按名称索引数据阅读器的成本稍微高一些。这主要有两个原因。

    • 典型的实现将字段信息存储在使用数字索引的数据结构中。必须执行映射操作才能将名称转换为数字。
    • 有些实现将对名称进行两遍查找。第一遍尝试在启用区分大小写的情况下匹配字段名。如果该传递失败,则第二次传递从关闭区分大小写开始。

    但是,在大多数情况下,按名称查找字段所带来的性能损失与数据库执行命令所需的时间相比要小得多。不要让性能惩罚决定您在名称和数字索引之间的选择。

    尽管有轻微的性能损失,我通常选择名称索引有两个原因。

    • 代码更容易阅读。
    • 代码对结果集模式中的更改更为宽容。

    如果您觉得名称索引的性能惩罚变得有问题(可能命令执行得很快,但返回了很多行),那么按名称查找一次数字索引,将其保存下来,并将其用于其余行。

        5
  •  2
  •   Will Marcouiller    14 年前

    我认为索引字段是更好的方法,如果它只是为了避免底层数据库中的字段名更改,这将需要重新编译应用程序,因为您对字段名进行了硬编码。

    对于每个字段,您需要手动检查空值。

    var dr = command.ExecuteQuery();
    
    if (dr.HasRows) {
        var customer = new Customer();
        // I'm assuming that you know each field's position returned from your query.
        // Where comes the importance to write all of your selected fields and not just "*" or "ALL".
        customer.Id = dr[0] == null || dr[0] == DBNull.Value ? null : Convert.ToInt32(dr[0]);
        ...
    }
    

    除此之外,它还允许您使用反射,使这个“getData()”方法更通用,提供一个类型(t),并为适当的类型获取适当的构造函数。绑定到每列的顺序是一些人唯一希望避免的事情,但在这种情况下,它变得值得。

        6
  •  0
  •   Julius A    14 年前

    序数的问题是,如果列的顺序发生了变化,并且您被迫修改DataReader的使用者代码,这与使用列名不同。

    我不认为在使用序号或列名时性能会有所提高,它更多的是关于最佳实践、编码标准和代码可维护性。