我可以从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();