我可以从DataReader创建匿名类型集合吗?
我有这个代码:
var query = "SELECT * FROM Cats"; var conn = new SqlConnection(sqlConnectionString); conn.Open(); var cmd = new SqlCommand(query); var reader = cmd.ExecuteReader(); while (reader.Read()) { var CatName = reader.GetString(0); var CatDOB = reader.GetDateTime(1); var CatStatus = reader.GetInt32(2); }
我想把行拉成一个匿名类型集合,我通常使用LINQ来迭代,但我不确定是否可能由于你每次都要调用.Read()
的方式下一行。
有没有办法做到这一点?
您可以创建辅助generics方法并让编译器推断类型参数:
private IEnumerable Select (DbDataReader reader, Func selector) { while(reader.Read()) { yield return selector(reader); } }
用法:
var items = SelectFromReader(reader, r => new { CatName = r.GetString(0), CarDOB = r.GetDateTime(1), CatStatus = r.GetInt32(2) });
您甚至可以在DbDataReader
上将该方法作为扩展方法:
public static IEnumerable Select (this DbDataReader reader, Func selector) { while (reader.Read()) { yield return selector(reader); } }
并使用它:
var items = reader.Select(r => new { CatName = r.GetString(0), CarDOB = r.GetDateTime(1), CatStatus = r.GetInt32(2) });
这是一个使用动态(我认为更容易使用)的例子,但有些人可能觉得不遵守你的问题的字母。
这样叫:
var result = SelectIntoList("SELECT * FROM Cats",sqlconnectionString);
你可以(像我一样)将它放在一个单独的文件中的静态类中,以便于维护。
public static IEnumerable SelectIntoList(string SQLselect, string connectionString, CommandType cType = CommandType.Text) { using (SqlConnection conn = new SqlConnection(connectionString)) { using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandType = cType; cmd.CommandText = SQLselect; conn.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) // read the first one to get the columns collection { var cols = reader.GetSchemaTable() .Rows .OfType() .Select(r => r["ColumnName"]); do { dynamic t = new System.Dynamic.ExpandoObject(); foreach (string col in cols) { ((IDictionary)t)[col] = reader[col]; } yield return t; } while (reader.Read()); } } conn.Close(); } } }
这是可能的 ,虽然不是特别整洁。 我们需要创建一个新方法,允许我们创建一个空序列,允许类型推断从初学者的虚拟值:
public static IEnumerable Empty (T dummyValue) { return Enumerable.Empty (); }
这让我们创建一个匿名类型的列表:
var list = Empty(new { CatName = "", CatDOB = DateTime.Today, CatStatus = 0 }).ToList();
(此处的项目未使用。)
现在我们可以将匿名类型添加到此列表中:
var cmd = new SqlCommand(query); var reader = cmd.ExecuteReader(); while (reader.Read()) { list.Add(new { CatName = reader.GetString(0), CatDOB = reader.GetDateTime(1), CatStatus = reader.GetInt32(2), }); }
当然,使用命名类型可能会更容易,所以我建议使用一个,除非有一个真正令人信服的理由不这样做。 如果您计划在其创建的范围之外使用列表,则尤其如此。
从技术上讲,它可能无法解答您的问题,但只是不使用读者。 而是使用SqlDataAdapter
填充DataSet,如果可以的话。 获取该DataSet的第0个表,并从Rows集合中选择一个新的匿名对象。
using System.Data; // and project must reference System.Data.DataSetExtensions var ds = new DataSet(); using (var conn = DbContext.Database.GetDbConnection()) using (var cmd = conn.CreateCommand()) { cmd.CommandType = CommandType.Text; cmd.CommandText = sqlText; conn.Open(); (new SqlDataAdapter(cmd)).Fill(ds); } var rows = ds.Tables[0].AsEnumerable(); // AsEnumerable() is the extension var anons = rows .Select(r => new { Val = r["Val"] }) .ToList();