从SqlDataReader中读取数据

180
我有一个 SQL Server 2008 数据库,我正在后台进行工作。我正在使用 ASP.NET/C# 进行开发。
SqlDataReader rdr = cmd.ExecuteReader();  
while (rdr.Read())  
{              
   //how do I read strings here????  
}

我知道读者有价值观。我的SQL命令是从表中选择只有1列。该列仅包含字符串。我想逐行读取读者中的字符串(行)。我该如何做到这一点?
13个回答

172
using(SqlDataReader rdr = cmd.ExecuteReader())
{
    while (rdr.Read())
    {
        var myString = rdr.GetString(0); //The 0 stands for "the 0'th column", so the first column of the result.
        // Do somthing with this rows string, for example to put them in to a list
        listDeclaredElsewhere.Add(myString);
    }
}

129
string col1Value = rdr["ColumnOneName"].ToString();
或者
string col1Value = rdr[0].ToString();

这些是object,所以您需要将它们强制转换或使用.ToString()


3
[]运算符返回一个对象,你需要将其转换为字符串。 - Scott Chamberlain
如果您使用像reader.GetString(0)这样的索引,它会使用您在查询中选择的第一列还是表中的第一列。我的表按顺序有3列:ID、Dir、Email。我的命令选择dir和email。reader.GetString(0)会检索dir还是ID?索引是基于SQL Server上的表本身还是基于您执行的从表中选择列的查询? - ss7
1
@shenk 索引基于您选择参数的顺序。无论如何,最好使用列名或别名(例如rdr["ID"]而不是rdr[0])。 - Mark Avenius
1
@MarkAvenius 过去,通过数字序数进行索引比使用列名/别名可以提高性能-不确定现在是否仍然如此。 - BaltoStar
4
@BaltoStar,这很有趣;我之前不知道这个。然而,根据性能的差异(尤其是与将数据通过网络传输相比较),我通常认为查看列名称的可读性和可维护性优于对性能的微小提高。谢谢! - Mark Avenius
@MarkAvenius 在 .NET 的早期,人们会进行基准测试,并发现使用数字序数可以获得巨大的性能提升(50%+),所有快速的数据库访问代码都会使用这个技巧。但随着时间的推移,微软可能已经改进了算法 - 我没有查看任何最近的基准测试。 - BaltoStar

37
将从数据库返回的列的名称放在 "ColumnName" 的位置。如果它是字符串,您可以使用.ToString()。如果它是其他类型,则需要使用System.Convert进行转换。
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
    string column = rdr["ColumnName"].ToString();
    int columnValue = Convert.ToInt32(rdr["ColumnName"]);
}

25
while(rdr.Read())
{
   string col=rdr["colName"].ToString();
}

它会工作


3
“toString() is not valid” 应该改为 “.ToString()”,仅供参考。 - MethodMan
1
@MethodMan 感谢您的信息。我根据您的建议编辑了我的答案。 - Mohini Mhetre
你好,我怎样才能按行获取对象而不是按列获取?例如 {id:1,name:'John'}。 - Binsoi
如果我想要像下面这样的东西怎么办。 if(rdr[0]){ //在这里做些什么 }else if(rdr[1]){ //在这里做些什么 } 我尝试将其转换为bool,但它会给出无效的转换错误。 - Fahad Ejaz Butt

21

我想分享一个助手方法,适用于那些需要它的人:

public static class Sql
{
    public static T Read<T>(DbDataReader DataReader, string FieldName)
    {
        int FieldIndex;
        try { FieldIndex = DataReader.GetOrdinal(FieldName); }
        catch { return default(T); }

        if (DataReader.IsDBNull(FieldIndex))
        {
            return default(T);
        }
        else
        {
            object readData = DataReader.GetValue(FieldIndex);
            if (readData is T)
            {
                return (T)readData;
            }
            else
            {
                try
                {
                    return (T)Convert.ChangeType(readData, typeof(T));
                }
                catch (InvalidCastException)
                {
                    return default(T);
                }
            }
        }
    }
}

使用方法:

cmd.CommandText = @"SELECT DISTINCT [SoftwareCode00], [MachineID] 
                    FROM [CM_S01].[dbo].[INSTALLED_SOFTWARE_DATA]";
using (SqlDataReader data = cmd.ExecuteReader())
{
    while (data.Read())
    {
        usedBy.Add(
            Sql.Read<String>(data, "SoftwareCode00"), 
            Sql.Read<Int32>(data, "MachineID"));
    }
}

这个辅助方法可以将任何值转换为你想要的类型,如果无法转换或数据库中的值为空,则结果将为null。


4
好的代码片段,我将其修改为扩展方法,并且它非常有效。reader.GetColumn<int>("M_ID"); - Ali Umair

20

对于单个结果:

if (reader.Read())
{
    Response.Write(reader[0].ToString());
    Response.Write(reader[1].ToString());
}

对于多个结果:

while (reader.Read())
{
    Response.Write(reader[0].ToString());
    Response.Write(reader[1].ToString());
}

10

我知道这可能有些陈旧,但如果您正在将SqlDataReader的内容读入一个类中,那么这将非常方便。阅读器和类的列名应该相同。

public static List<T> Fill<T>(this SqlDataReader reader) where T : new()
        {
            List<T> res = new List<T>();
            while (reader.Read())
            {
                T t = new T();
                for (int inc = 0; inc < reader.FieldCount; inc++)
                {
                    Type type = t.GetType();
                    string name = reader.GetName(inc);
                    PropertyInfo prop = type.GetProperty(name);
                    if (prop != null)
                    {
                        if (name == prop.Name)
                        {
                            var value = reader.GetValue(inc);
                            if (value != DBNull.Value)
                            { 
                                prop.SetValue(t, Convert.ChangeType(value, prop.PropertyType), null);
                            }
                            //prop.SetValue(t, value, null);

                        }
                    }
                }
                res.Add(t);
            }
            reader.Close();

            return res;
        }

这应该是推荐的答案。返回一个已键入列表的非常通用的方法。 - kotpal

9

我认为在这里使用SqlDataReader是不明智的;ADO.NET有很多边缘情况和复杂性,根据我的经验,大多数手写的ADO.NET代码都存在至少一个问题(通常是微妙和上下文相关的问题)。

有一些工具可以避免这种情况。例如,在这种情况下,您想要读取一个字符串列。Dapper使得这完全没有痛苦:

var region = ... // some filter
var vals = connection.Query<string>(
    "select Name from Table where Region=@region", // query
    new { region } // parameters
).AsList();

Dapper在处理ADO.NET中的所有参数化、执行和行处理 - 以及许多其他繁琐的细节。 <string> 可以替换为 <SomeType>,以将整个行实例化为对象。

8

实际上,我自己想出了这个方法,可以这样做:

while (rdr.read())
{  
  string str = rdr.GetValue().ToString().Trim();  
}

1
我不认为这种方法比其他方法更复杂。在问题中没有提到Trim(),所以它出现在这里但不在其他答案中。 - jwg

7
简单来说,如果您的查询返回列名并且该列包含一个字符串:
while (rdr.Read())
{
    string yourString = rdr.getString("column_name")
}

1
目前,阅读器上的“.getXXX”方法仅接受整数序数。 - Cos Callis
我不确定这是否是新的东西,但目前我能够使用这种方法,并且我认为这是最好的,因为您不需要处理打字,也可以更好地识别您想要获取的值。 - Ernesto Alfonso

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