简体   繁体   English

我可以从DataReader创建匿名类型集合吗?

[英]Can I create an anonymous type collection from a DataReader?

I have this code: 我有这个代码:

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);
}

I'd like to pull the rows out into an anonymous type collection, which I'd normally do using LINQ to iterate, but I an not sure if it's possible due to the way you have to call .Read() each time to get the next row. 我想将行拉出到一个匿名类型集合中,我通常使用LINQ进行迭代,但我不确定是否可能由于你必须每次调用.Read()的方式下一行。

Is there a way to do this? 有没有办法做到这一点?

You can create helper generic method and let compiler infer type parameter: 您可以创建辅助泛型方法并让编译器推断类型参数:

private IEnumerable<T> Select<T>(DbDataReader reader, Func<DbDataReader, T> selector)
{
    while(reader.Read())
    {
        yield return selector(reader);
    }
}

usage: 用法:

var items = SelectFromReader(reader, r => new { CatName = r.GetString(0), CarDOB = r.GetDateTime(1), CatStatus = r.GetInt32(2) });

You can even make the method an extension method on DbDataReader : 您甚至可以在DbDataReader上将该方法作为扩展方法:

public static IEnumerable<T> Select<T>(this DbDataReader reader, Func<DbDataReader, T> selector)
{
    while (reader.Read())
    {
        yield return selector(reader);
    }
}

and use it like that: 并使用它:

var items = reader.Select(r => new { CatName = r.GetString(0), CarDOB = r.GetDateTime(1), CatStatus = r.GetInt32(2) });

Here is an example of doing it with dynamic (which I think is easier to work with) but some may feel does not adhere to the letter of your question. 这是一个使用动态(我认为更容易使用)的例子,但有些人可能觉得不遵守你的问题的字母。

Call it like this: 像这样称呼它:

var result = SelectIntoList("SELECT * FROM Cats",sqlconnectionString);

You could (like I did) put it into a static class in a separate file for easier maintanence. 你可以(像我一样)将它放在一个单独的文件中的静态类中,以便于维护。

public static IEnumerable<dynamic> 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<DataRow>()
                       .Select(r => r["ColumnName"]);

          do
          {
            dynamic t = new System.Dynamic.ExpandoObject();

            foreach (string col in cols)
            {
              ((IDictionary<System.String, System.Object>)t)[col] = reader[col];
            }

            yield return t;
          } while (reader.Read());
        }
      }

      conn.Close();
    }
  }
}

It's possible , although not particularly neat. 这是可能的 ,虽然不是特别整洁。 We'll need to create a new method that will allow us to create an empty sequence that allows for type inference off of a dummy value for starters: 我们需要创建一个新方法,允许我们创建一个空序列,允许类型推断偏离初学者的虚拟值:

public static IEnumerable<T> Empty<T>(T dummyValue)
{
    return Enumerable.Empty<T>();
}

This lets us create a list of an anonymous type: 这让我们可以创建一个匿名类型的列表:

var list = Empty(new
{
    CatName = "",
    CatDOB = DateTime.Today,
    CatStatus = 0
}).ToList();

(The item here isn't used.) (此处的项目未使用。)

Now we can add our anonymous types to this list: 现在我们可以将匿名类型添加到此列表中:

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),
    });
}

Of course, using a named type would likely be easier, so I would suggest using one unless there is a real compelling reason not to do so. 当然,使用命名类型可能会更容易,所以我建议使用一个,除非有一个真正令人信服的理由不这样做。 That is especially true if you plan to use the list outside of the scope it's created in. 如果您计划在其创建的范围之外使用列表,则尤其如此。

Technically, it may not answer your question, but simply don't use a reader. 从技术上讲,它可能无法解答您的问题,但只是不使用读者。 Instead use a SqlDataAdapter to Fill a DataSet, if you can. 而是使用SqlDataAdapter填充数据集,如果可以的话。 Take the 0th Table of that DataSet, and select a new anonymous object from the Rows collection. 获取该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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM