2010-05-21 58 views
15

当从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"]或更多el索引方式导致性能下降。

然而,虽然数据实体的所有引用越来越强类型的,我觉得这个更不舒服。我也知道上述不检查DBNull

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

+0

相关性能问题:[sqldatareader-are-these-two-the-same-which-one-is-faster](http:// stackoverflow。com/questions/7831574/sqldatareader-are-these-two-the-same-which-one-is-faster) – nawfal 2013-11-28 07:56:18

回答

25

在这种情况下有可能争论双方。正如其他人已经指出的那样,如果有人更改底层数据库中列的顺序,使用该名称更具可读性并且不会中断。但有人可能会争辩说,如果某人更改了底层数据库中的列名,那么使用序号具有不会中断的优势。不过,我更喜欢前者的观点,并认为列名的可读性参数胜过了第二个参数。而且名称的另一个参数是它可以“自我检测”错误。如果有人确实更改了字段名称,那么代码有更好的中断机会,而不是在错误的字段中显示工作的细微错误。

看起来很明显,但也许值得一提的是一个既有自我检测错误又有序号性能的使用案例。如果在SQL显式指定SELECT列表中,然后使用序号不会因为在代码中陈述保证的顺序是一个问题:

SELECT name, address, phone from mytable 

在这种情况下,这将是相当安全的使用序号来访问数据。如果有人在桌子周围移动场地,这并不重要。如果有人更改名称,那么SQL语句在运行时会产生错误。

最后一点。我刚刚参加了我帮助编写的供应商的测试。该测试读取100万行并访问每个记录上的“姓氏”字段(与值进行比较)。 rdr[“lastname”]的使用耗时3301毫秒,而rdr.GetString(1)耗费2640毫秒(约加速25%)。在这个特定的提供程序中,名称查找使用排序查找将名称翻译为序号。

+0

这里您有一些优点。 – 2010-05-24 16:29:52

+4

+1提供指标。 – 2010-05-24 20:47:14

+0

就我个人而言,我认为重新排列列(读取,插入新字段并因此重新排列现有列的位置)比重命名列名更为常见。所以你的建议+1。 – nawfal 2015-07-31 13:58:53

0

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

我不认为使用序号或列名时,有一个性能增益,它更多的是对最佳实践和编码标准和代码的可维护性真的

4

我总是以字符串名称的方法去只是因为阅读的代码清洁器。不得不从心理上解析索引到列名是非常可怕的。

+4

使用列名称也消除了“幻数”,并使程序员的工作更轻松。使用序数是一种过早的微观优化。 – Malfist 2010-05-21 13:21:16

+1

+1。列名更改比其次序更难得 – abatishchev 2010-05-21 14:38:59

+0

这个问题有两个部分,性能方面和可维护性/可读性。字符串名称不能很好地缩放。 @Malfist将某些“过早微优化”标记为与您的代码相关。如果你的代码性能很高,那么这绝对是瓶颈。 – Josh 2010-05-22 11:34:14

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),并得到了正确的类型适当的构造函数。对每个列的顺序的绑定是有些人希望避免的唯一事情,但在这种情况下,它变得有价值。

4

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

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

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

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

  • 该代码更容易阅读。
  • 该代码对结果集模式中的更改容忍度更高。

如果你觉得自己的名字索引的性能损失成为问题(也许是命令迅速地执行,而是返回很多行),然后通过名称查找的数字索引一次,保存它拿走,并用它来剩下的行。

13

字符串名称查找比序号调用要昂贵得多,但比硬编码序号更易于维护和“脆弱”。所以这就是我一直在做的事情。这是两全其美的。我不必记住序号值或关心列顺序是否改变,但我可以获得使用序号的性能优势。

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()在这些扩展方法中,你可以做任何你需要的东西来确信你能够获得预期的价值。

+0

Upvoted两全其美。 – nawfal 2015-07-31 18:36:57

+1

当我不得不潜入ADO.NET时,我喜欢这种方法。我创建一个内联对象并将所有序号映射为友好名称,以便我可以执行像'reader.GetString(ordinals.CustomerName)' – 2016-04-19 14:16:17