通过DataReader获取多个结果集
我们知道,ado.net的DataReader提供一种从数据库读取行的只进流的方式。通常情况下,我们都是针对一个结果集进行处理。其实通过DataReader的NextResult方法,我们可以批处理T-SQL,也就是可以获取处理两个或者多个结果集。下面通过一个简单的示例,来简单说明一下这个功能。
1、数据表
我们还是沿用这一篇里的Person表。在Person表里,我们定义了自增长的Id,姓、名、身高和体重,它们对应的字段和实体里的属性命名是一样的。
2、实体类
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public double Weight { get; set; }
public double Height { get; set; }
/// <summary>
/// 总记录数
/// </summary>
public long TotalCount { get; set; }
}
需要说明的是,TotalCount属性是我们自己根据统计需要,添加的用来统计符合条件的人的总记录数,并不是Person表的一个实际字段。
3、数据实体转换
{
private const string strSqlCon = @"Data Source=.\sqlexpress; Initial Catalog=TestDb; User Id=sa; Password=123456;";
/// <summary>
/// 数据表项转换为对应实体 (操作两个结果集 有总记录数)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objType"></param>
/// <param name="strSelectSql"></param>
/// <returns></returns>
public static IList<T> Select<T>(Type objType, string strSelectSql) where T : class, new()
{
IList<T> listModels = new List<T>();
using (SqlConnection sqlConn = new SqlConnection(strSqlCon))
{
sqlConn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = sqlConn;
cmd.CommandText = strSelectSql;
IDataReader rdr = cmd.ExecuteReader();
Hashtable htColumns = CreateHashColumnName(rdr);
while (rdr.Read())
{
Object obj = Activator.CreateInstance(objType);
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo propInfo in properties)
{
if (htColumns.Contains(propInfo.Name.ToUpper()) == false)
{
continue;
}
int index = rdr.GetOrdinal(propInfo.Name);
if (index > -1 && rdr.GetValue(index) != System.DBNull.Value)
propInfo.SetValue(obj, rdr.GetValue(index), null);
}
T model = default(T);
model = obj as T;
listModels.Add(model);
}
bool isNext = rdr.NextResult();//前进到下一个结果集
htColumns = CreateHashColumnName(rdr);//改变hashtable的对应列名
while (rdr.Read() && isNext)
{
for (int i = 0; i < listModels.Count; i++)
{
PropertyInfo[] props = listModels[i].GetType().GetProperties();
foreach (PropertyInfo pi in props)
{
if (htColumns.Contains(pi.Name.ToUpper()))//取总记录数
{
int index = rdr.GetOrdinal(pi.Name);
if (index > -1 && rdr.GetValue(index) != System.DBNull.Value)
pi.SetValue(listModels[i], rdr.GetValue(index), null);
break;//根据实际情况确定break需不需要注释掉
}
}
}
}
}
return listModels;
}
private static Hashtable CreateHashColumnName(IDataReader rdr)
{
int len = rdr.FieldCount;
Hashtable ht = new Hashtable(len);
for (int i = 0; i < len; i++)
{
string columnName = rdr.GetName(i).ToUpper(); //不区分大小写
string columnRealName = rdr.GetName(i);
if (ht.ContainsKey(columnName) == false)
{
ht.Add(columnName, columnRealName);
}
}
return ht;
}
}
上面的代码中,注释写的很清楚,通过NextResult方法后,我们再次获取结果集的列(示例中保持在hashtable里),利用第一次获得结果集的方法和原理,给实体赋值。
4、前台调用
很简单,通过一个带分号的sql语句,获得一个实体集合。
string sql = "SELECT TOP 10 [Id] ,[FirstName],[LastName],[Weight],[Height] FROM Person (NOLOCK); SELECT COUNT(Id) AS TotalCount FROM Person (NOLOCK) ";
IList<Person> listPersons = SqlHelper.Select<Person>(typeof(Person), sql);//操作两个结果集
有图有真相:
从图中我们可以清楚地看到,测试数据库里的总人数是1999918人。
最后,请教最近使用linq2sql一点困扰我的地方:很多人津津乐道的Skip...Take分页(本质上据说就是二次top),好像无法直接取得总记录数(TotalCount),但是正常项目中都会通过总记录数确定分页效果。除了直接通过linq查询取Count之外,不知道大家有没有其他更好的方法,比如窗口函数row number,或者就像本文所表达的那样,批处理一次查询就搞定的那种?
作者:Jeff Wong
出处:http://jeffwongishandsome.cnblogs.com/
本文版权归作者和博客园共有,欢迎围观转载。转载时请您务必在文章明显位置给出原文链接,谢谢您的合作。