DataReader – 硬编码序数?

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

DataReader返回数据的最强大的方法是什么?

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

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

 SELECT name, address, phone from mytable 

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

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

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

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

我总是采用字符串名称方法,因为阅读代码更清晰。 必须在心理上解析索引到列名是可怕的。

按名称索引数据读取器稍贵一些。 这有两个主要原因。

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

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

尽管性能略有下降,但我通常选择名称索引有两个原因。

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

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

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

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

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

除此之外,它还允许您使用reflection并使这个“GetData()”方法更通用,提供typeof(T)并获得正确类型的正确构造函数。 绑定到每列的顺序是有些人希望避免的唯一一件事,但在这种情况下,它变得有价值。

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

我不认为使用序数或列名称会有性能提升,它更多地依赖于最佳实践和编码标准以及代码可维护性