Can I create an anonymous type collection from a DataReader?

后端 未结 4 541
没有蜡笔的小新
没有蜡笔的小新 2021-02-09 06:22

I have this code:

var query = \"SELECT * FROM Cats\";

var conn = new SqlConnection(sqlConnectionString);

conn.Open();

var cmd = new SqlCommand(query);
var rea         


        
相关标签:
4条回答
  • 2021-02-09 07:01

    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.

    0 讨论(0)
  • 2021-02-09 07:06

    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:

    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) });
    
    0 讨论(0)
  • 2021-02-09 07:15

    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. Take the 0th Table of that DataSet, and select a new anonymous object from the Rows collection.

    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();
    
    0 讨论(0)
  • 2021-02-09 07:15

    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();
        }
      }
    }
    
    0 讨论(0)
提交回复
热议问题