C# 从DataReader检索数据的最有效方法是什么?

C# 从DataReader检索数据的最有效方法是什么?,c#,performance,sqldatareader,data-access,C#,Performance,Sqldatareader,Data Access,我花了很多时间查询数据库,然后从查询中构建对象集合。为了提高性能,我倾向于使用Datareader,代码如下所示: while(rdr.Read()){ var myObj = new myObj(); myObj.Id = Int32.Parse(rdr["Id"].ToString(); //more populating of myObj from rdr myObj.Created = (DateTime)rdr["Created"]; } 对于像

我花了很多时间查询数据库,然后从查询中构建对象集合。为了提高性能,我倾向于使用Datareader,代码如下所示:

while(rdr.Read()){
    var myObj = new myObj();

    myObj.Id = Int32.Parse(rdr["Id"].ToString();

    //more populating of myObj from rdr

    myObj.Created = (DateTime)rdr["Created"];
}
对于像
DateTime
这样的对象,我只需将rdr值强制转换为所需的类,但对于像
int
这样的值类型,这是无法做到的,因此(IMHO)需要费劲的
ToString()
后跟
int.Parse(…)

当然还有另一种选择:

myObj.Id = rdr.GetInt32(rdr.GetOrdinal("Id"));
它看起来更干净,不需要调用
ToString()


我和一位同事今天讨论了这一点——他建议在上述代码中访问两次
rdr
可能比我以前的skool方式效率更低——有人能证实或否认这一点,并建议上面哪一种是做这类事情的
最佳方式吗?我特别欢迎@JonSkeet;-)的回答

这对读者来说是最有意义的。它是否是最“有效的”(你实际上是在调用两个函数而不是一个函数,它的意义不会像强制转换,然后调用一个函数那样大)。理想情况下,如果不影响性能,您应该选择看起来更具可读性的选项

var ordinal = rdr.GetOrdinal("Id");
var id = rdr.GetInt32(ordinal);
myObj.Id = id;

这对读者来说是最有意义的。它是否是最“有效的”(你实际上是在调用两个函数而不是一个函数,它的意义不会像强制转换,然后调用一个函数那样大)。理想情况下,如果不影响性能,您应该选择看起来更具可读性的选项

var ordinal = rdr.GetOrdinal("Id");
var id = rdr.GetInt32(ordinal);
myObj.Id = id;

我怀疑会有非常明显的性能差异,但您可以通过将每一行从循环中取出来避免在每一行上查找名称。这可能是您能够实现的最佳目标:

int idIdx = rdr.GetOrdinal("Id");
int createdIdx = rdr.GetOrdinal("Created");

while(rdr.Read())
{
    var myObj = new myObj();

    myObj.Id = rdr.GetFieldValue<int>(idIdx);

    //more populating of myObj from rdr

    myObj.Created = rdr.GetFieldValue<DateTime>(createdIdx);
}
intIDIDX=rdr.GetOrdinal(“Id”);
int createdIdx=rdr.GetOrdinal(“已创建”);
while(rdr.Read())
{
var myObj=新的myObj();
myObj.Id=rdr.GetFieldValue(idIdx);
//来自rdr的myObj的更多人口
myObj.Created=rdr.GetFieldValue(createdIdx);
}

我怀疑会有明显的性能差异,但只要将每一行从循环中取出,就可以避免在每一行上查找名称。这可能是您能够实现的最佳目标:

int idIdx = rdr.GetOrdinal("Id");
int createdIdx = rdr.GetOrdinal("Created");

while(rdr.Read())
{
    var myObj = new myObj();

    myObj.Id = rdr.GetFieldValue<int>(idIdx);

    //more populating of myObj from rdr

    myObj.Created = rdr.GetFieldValue<DateTime>(createdIdx);
}
intIDIDX=rdr.GetOrdinal(“Id”);
int createdIdx=rdr.GetOrdinal(“已创建”);
while(rdr.Read())
{
var myObj=新的myObj();
myObj.Id=rdr.GetFieldValue(idIdx);
//来自rdr的myObj的更多人口
myObj.Created=rdr.GetFieldValue(createdIdx);
}

实际上,在使用
SqlDataReader
的方式上存在性能上的差异,但它们在其他地方。即该方法接受
命令行为。SequentialAccess

为DataReader提供一种处理包含具有大二进制值的列的行的方法。SequentialAccess使DataReader能够以流的形式加载数据,而不是加载整行。然后,可以使用GetBytes或GetChars方法指定开始读取操作的字节位置,以及返回数据的有限缓冲区大小。 当您指定SequentialAccess时,您需要按照返回的顺序读取列,尽管您不需要读取每一列。一旦您读取了返回数据流中的某个位置,则无法再从DataReader读取该位置或该位置之前的数据。使用OleDbDataReader时,可以重新读取当前列值,直到读取超过它为止。使用SqlDataReader时,只能读取一次列值


如果不使用大的二进制值,则差别很小。获取字符串并进行解析是次优的,如果是true,则最好使用
GetInt32()
来获取值,因为。但是,在大多数应用程序上,差异不应该是显而易见的,除非你的应用程序实际上除了读取巨大的数据集之外什么都不做。大多数应用程序都不是这样的。专注于优化数据库调用本身(即快速执行查询)将在99.9999%的情况下获得更大的好处。

实际上,在使用
SqlDataReader
的方式上存在性能差异,但它们在其他地方。即该方法接受
命令行为。SequentialAccess

为DataReader提供一种处理包含具有大二进制值的列的行的方法。SequentialAccess使DataReader能够以流的形式加载数据,而不是加载整行。然后,可以使用GetBytes或GetChars方法指定开始读取操作的字节位置,以及返回数据的有限缓冲区大小。 当您指定SequentialAccess时,您需要按照返回的顺序读取列,尽管您不需要读取每一列。一旦您读取了返回数据流中的某个位置,则无法再从DataReader读取该位置或该位置之前的数据。使用OleDbDataReader时,可以重新读取当前列值,直到读取超过它为止。使用SqlDataReader时,只能读取一次列值


如果不使用大的二进制值,则差别很小。获取字符串并进行解析是次优的,如果是true,则最好使用
GetInt32()
来获取值,因为。但是,在大多数应用程序上,差异不应该是显而易见的,除非你的应用程序实际上除了读取巨大的数据集之外什么都不做。大多数应用程序都不是这样的。专注于优化数据库调用本身(即快速执行查询)将在99.9999%的情况下获得更大的好处。

为此,我通常引入一个
记录集
类:

public class MyObjRecordSet
{
    private readonly IDataReader InnerDataReader;
    private readonly int OrdinalId;
    private readonly int OrdinalCreated;

    public MyObjRecordSet(IDataReader dataReader)
    {
        this.InnerDataReader = dataReader;
        this.OrdinalId = dataReader.GetOrdinal("Id");
        this.OrdinalCreated = dataReader.GetOrdinal("Created");
    }

    public int Id
    {
        get
        {
            return this.InnerDataReader.GetInt32(this.OrdinalId);
        }
    }

    public DateTime Created
    {
        get
        {
            return this.InnerDataReader.GetDateTime(this.OrdinalCreated);
        }
    }

    public MyObj ToObject()
    {
        return new MyObj
        {
            Id = this.Id,
            Created = this.Created
        };
    }

    public static IEnumerable<MyObj> ReadAll(IDataReader dataReader)
    {
        MyObjRecordSet recordSet = new MyObjRecordSet(dataReader);
        while (dataReader.Read())
        {
            yield return recordSet.ToObject();
        }
    }
}
公共类记录集
{
私有只读IDataReader InnerDataReader;
私有只读int-OrdinalId;
已按顺序创建私有只读int;
公共记录集(IDataReader数据读取器)
{
这是我的
myObj.Id = (int) (decimal) rdr["Id"];