IDataReader.GetOrdinal或IDataReader [ColumnName]是什么?

3

我有两种情况需要从IDataReader对象中提取信息

情况1 - 长度,计算序数,然后解析字符串

public static string GetString(IDataReader rdr, string columnName)
{
    int ordinal = rdr.GetOrdinal(columnName);
    if (rdr.IsDBNull(ordinal))
    {
        return string.Empty;
    }
    return (string)rdr[ordinal];
}

案例 - 2,简便方法,获取数据而无需计算序数

public static string GetString(IDataReader rdr, string columnName)
{
    return (string)rdr[columnName];
}

哪种技术应该被优先选择,为什么?是否有特定的上下文需要考虑?

个人而言,我会使用第二种方法。如果您不关心检查列名是否存在,那么这是一个好的选择。您总是能够获得非空值吗? - MethodMan
Pavel所说的是正确的,但是你的函数不同之处在于它们返回string.Empty和null,这可能会导致在使用返回值时产生意外的结果。 - Eric H
1
@EricH 第二个示例将针对null数据抛出InvalidCastException异常。您不能将DBNull转换为字符串。 - phoog
可能是重复的问题:.NET SqlDataReader Item[] vs. GetString(GetOrdinal())? - nawfal
3个回答

4

SqlDataReaderthis[string name] 看起来像:

public override object this[string name]
{
    get
    {
        return this.GetValue(this.GetOrdinal(name));
    }
}

所以它在内部计算序数,无论使用哪种方式都没有区别。

更新

您可以将代码重写为:

public static string GetString(IDataReader rdr, string columnName)
{
    return (rdr[columnName] as String)??String.Empty;
}

你想分享示例链接吗? - Pankaj
@Pankaj 注意,如果你像这里所示的缓存GetOrdinal:https://dev59.com/fG025IYBdhLWcg3wZ1Ry 那么reader[i]将显示较小的性能提升。 - nawfal

2
这是你的第一个方法的MSIL代码示例:

这是你的第一个方法的MSIL代码示例:

.method public hidebysig static string  GetString(class [System.Data]System.Data.IDataReader rdr,
                                                  string columnName) cil managed
{
  // Code size       49 (0x31)
  .maxstack  2
  .locals init ([0] int32 ordinal,
           [1] string CS$1$0000,
           [2] bool CS$4$0001)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  callvirt   instance int32 [System.Data]System.Data.IDataRecord::GetOrdinal(string)
  IL_0008:  stloc.0
  IL_0009:  ldarg.0
  IL_000a:  ldloc.0
  IL_000b:  callvirt   instance bool [System.Data]System.Data.IDataRecord::IsDBNull(int32)
  IL_0010:  ldc.i4.0
  IL_0011:  ceq
  IL_0013:  stloc.2
  IL_0014:  ldloc.2
  IL_0015:  brtrue.s   IL_0020
  IL_0017:  nop
  IL_0018:  ldsfld     string [mscorlib]System.String::Empty
  IL_001d:  stloc.1
  IL_001e:  br.s       IL_002f
  IL_0020:  ldarg.0
  IL_0021:  ldloc.0
  IL_0022:  callvirt   instance object [System.Data]System.Data.IDataRecord::get_Item(int32)
  IL_0027:  castclass  [mscorlib]System.String
  IL_002c:  stloc.1
  IL_002d:  br.s       IL_002f
  IL_002f:  ldloc.1
  IL_0030:  ret
} // end of method Program::GetString

而对于你的第二种方法:
.method public hidebysig static string  GetStringShort(class [System.Data]System.Data.IDataReader rdr,
                                                       string columnName) cil managed
{
  // Code size       18 (0x12)
  .maxstack  2
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldarg.1
  IL_0003:  callvirt   instance object [System.Data]System.Data.IDataRecord::get_Item(string)
  IL_0008:  castclass  [mscorlib]System.String
  IL_000d:  stloc.0
  IL_000e:  br.s       IL_0010
  IL_0010:  ldloc.0
  IL_0011:  ret
} // end of method Program::GetStringShort

因此,这些方法显然不相同。至于哪种更好,你没有说明想要计算序数的原因,所以很难确定哪种方法对你的情况更好。


0

我认为实际上没有什么区别(它们做的事情是一样的),第二个对我来说更易读,因为介绍“索引”的方式会让那些不知道读者如何工作的人感到困惑。我认为应该隐藏不必要的复杂性。你应该再进一步,使其成为一个通用的可以在任何地方使用的东西,就像这样:

    private static T FromDbValue<T>(IDataReader rdr, string columnName)
    {
        var value = rdr[columnName];

        if (value == DBNull.Value)
        {
            return default(T);
        }

        return (T)value;
    }

调用它很容易:

var someString = FromDbValue<string>(rdr, "CustomerName");

编辑: 在你的第二个示例中,你没有检查DBNull,所以在空值上进行转换会失败。然而,一般来说这两种方法是相同的。

这个问题与泛型无关,它是非泛型的。你有什么链接想分享吗? - Pankaj
没有了,我删掉了一些多余的代码,错过了那个。已经删除。谢谢。 - Arbiter
请在相同的上下文中更新答案,否则您可能会遭受负评。 - Pankaj
我不知道你的意思?我为我的方法提供了一个使用示例。如果你不能使用泛型,请使用你的方法。 - Arbiter
这可能是一个两行函数:var value = rdr[columnName]; return (value == DBNull.Value) ? default(T) : return (T)value; - Eric H
@EricH 是的,那也可以,但我通常避免三元运算符,因为它可能更难阅读。 - Arbiter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接