我花了很多时间查询数据库,然后从查询中构建对象集合。为了性能,我倾向于使用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方式 - 任何人都可以确认或否认这一点并建议以上哪一项这种事情的best
方式是什么?我特别欢迎来自@JonSkeet的答案; - )
答案 0 :(得分:3)
我怀疑会有一个非常明显的性能差异,但你可以简单地通过将其提升出循环来避免每行上的名称查找。这可能是您能够实现的最佳目标:
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);
}
答案 1 :(得分:2)
我通常会为此目的引入一个RecordSet
类:
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();
}
}
}
用法示例:
List<MyObj> myObjects = MyObjRecordSet.ReadAll(rdr).ToList();
答案 2 :(得分:1)
这对读者来说是最有意义的。它是否是最“高效”的(你实际上是在调用两个函数而不是一个函数,它不会像强制转换那样重要,然后调用函数)。理想情况下,如果不损害您的性能,您应该选择看起来更具可读性的选项。
var ordinal = rdr.GetOrdinal("Id");
var id = rdr.GetInt32(ordinal);
myObj.Id = id;
答案 3 :(得分:1)
实际上,您使用SqlDataReader
的方式的性能存在差异,但它们位于其他位置。即ExecuteReader
方法接受CommandBehavior.SequentialAccess
:
为DataReader提供一种处理包含具有大二进制值的列的行的方法。 SequentialAccess不是加载整行,而是使DataReader能够将数据作为流加载。然后,您可以使用GetBytes或GetChars方法指定用于启动读取操作的字节位置,以及用于返回数据的有限缓冲区大小。 指定SequentialAccess时,您需要按照返回的顺序读取列,但不需要读取每列。一旦读取了返回的数据流中的某个位置,就无法再从DataReader中读取该位置或之前的数据。使用OleDbDataReader时,您可以重读当前列值,直到读取它为止。使用SqlDataReader时,只能读取一次列值。
如果你不使用大二进制值,那么它就会产生很小的差别。获取字符串和解析不是最理想的,因为rdr.SqlInt32(column)
,最好使用NULL
而不是GetInt32()
来获取值。但是在大多数应用程序中差异不应该是显而易见的,unles你的应用程序正在做什么没有别的但是阅读庞大的数据集。大多数应用程序都不会那样。专注于优化数据库调用本身(即快速执行查询)将获得99.9999%的更多好处。
答案 4 :(得分:1)
对于像DateTime这样的对象,我只是将rdr值转换为所需的类,但是对于像int这样的值类型不能这样做
事实并非如此:DateTime也是一种值类型,如果字段属于预期类型且不为null,则以下两种方式都以相同的方式工作:
myObj.Id = (int) rdr["Id"];
myObj.Created = (DateTime)rdr["Created"];
如果它不适合你,你正在阅读的字段可能是NULL吗?或者不是必需的类型,在这种情况下你需要施放两次。例如。对于SQL NUMERIC字段,您可能需要:
myObj.Id = (int) (decimal) rdr["Id"];