15

从 a 返回数据时,DataReader我通常会使用 上的序号引用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.

从 a 返回数据的最可靠方法是DataReader什么?

4

6 回答 6

25

在这种情况下,双方都有可能发生争执。正如其他人已经指出的那样,使用该名称更具可读性,并且如果有人更改底层数据库中列的顺序也不会中断。但也有人可能会争辩说,如果有人更改底层数据库中的列名,使用序数的优点是不会中断。不过,我更喜欢前一个参数,并且认为列名的可读性参数通常胜过第二个参数。名称的另一个论点是它可以“自我检测”错误。如果有人确实更改了字段名称,那么代码更有可能被破坏,而不是在读取错误字段时出现看起来可以工作的细微错误。

这似乎很明显,但也许值得一提的是一个同时具有自我检测错误和序数性能的用例。如果您在 SQL 中显式指定 SELECT 列表,那么使用序数不会有问题,因为代码中的语句保证了顺序:

SELECT name, address, phone from mytable

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

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

于 2010-05-24T15:45:39.593 回答
15

字符串名称查找比序数调用更昂贵,但比对序数进行硬编码更易于维护且更不“脆弱”。这就是我一直在做的事情。这是两全其美的。我不必记住序数值或关心列顺序是否发生变化,但我获得了使用序数的性能优势。

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() 在这些扩展方法中,您可以做任何您需要确信您获得预期值的事情。

于 2010-05-22T11:12:11.490 回答
4

我总是使用字符串名称方法,只是因为阅读代码更干净。必须在精神上将索引解析为列名是可怕的。

于 2010-05-21T13:13:16.573 回答
3

按名称索引数据读取器的成本略高。这有两个主要原因。

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

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

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

  • 代码更容易阅读。
  • 代码更能容忍结果集模式的变化。

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

于 2010-05-21T14:43:32.197 回答
2

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

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

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()”方法更通用,提供 typeof(T) 并为正确的类型获取正确的构造函数。绑定到每列的顺序是一些人希望避免的唯一一件事,但在这种情况下,它变得有价值。

于 2010-05-21T13:19:40.757 回答
0

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

我认为使用序号或列名时不会有性能提升,更多的是最佳实践和编码标准以及代码可维护性

于 2010-05-21T13:12:33.820 回答