How can I map the results of a sql query onto objects?

后端 未结 7 1652
灰色年华
灰色年华 2021-02-01 20:37

Currently, I am using something like this:

    try
    {
      dr = SQL.Execute(sql);

      if(dr != null) {
         while(dr.Read()) {
           CustomObject         


        
相关标签:
7条回答
  • 2021-02-01 21:01

    The following function accepts a SQL string and an object, it requires the object to have a property for each column in the select statement. The object must be instantiated.

    public object SqlToSingleObject(string sSql, object o)
    {
        MySql.Data.MySqlClient.MySqlDataReader oRead;
        using (ConnectionHelper oDb = new ConnectionHelper())
        {
            oRead = oDb.Execute(sSql);
            if (oRead.Read())
            {
                for (int i = 0; i < oRead.FieldCount; i++)
                {
                    System.Reflection.PropertyInfo propertyInfo = o.GetType().GetProperty(oRead.GetName(i));
                    propertyInfo.SetValue(o, Convert.ChangeType(oRead[i], propertyInfo.PropertyType), null);
                }
    
                return o;
            }
            else
            {
                return null;
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-01 21:02

    You should look into MicroORMs. Unlike regular ORMs, that provide an SDL you must use, MicroORMs allow you to use your own SQL queries and only provide the mapping from SQL result sets to C# objects and from C# objects to SQL parameters.

    My favorite is PetaPoco, which also provides a query builder that uses your own SQL but does some neat manipulation of parameter numbers.

    0 讨论(0)
  • 2021-02-01 21:02

    When searching for this answer I found that you can use Dapper library: https://dapper-tutorial.net/knowledge-base/44980945/querying-into-a-complex-object-with-dapper

    You can use something like this:

            using (var connection = new SqlConnection(ConnectionString))
            {
                connection.Open();
                IList<CustomObject> result = connection.Query<CustomObject>(sql, commandType: CommandType.Text).ToList();
            }
    
    0 讨论(0)
  • 2021-02-01 21:05

    You can achieve by creating a generic method for your requirement. Also you can make your new method as the extension for the data table.

        public static List<T> ToList<T>(this DataTable table) where T : class, new()
    {
        try
        {
            List<T> list = new List<T>();
    
            foreach (var row in table.AsEnumerable())
            {
                T obj = new T();
    
                foreach (var prop in obj.GetType().GetProperties())
                {
                    try
                    {
                        PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
                        propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
                    }
                    catch
                    {
                        continue;
                    }
                }
    
                list.Add(obj);
            }
    
            return list;
        }
        catch
        {
            return null;
        }
    }
    

    }

    Usage:

        DataTable dtCustomer = GetCustomers();
        List<CustomObject> CustomObjectList = dtCustomer.ToList<CustomObject>();
    
    0 讨论(0)
  • 2021-02-01 21:07

    One simple solution would be to make a constructor for your CustomObject that takes a DataRow (from the example, so if it's another class, please correct me).

    And in your new constructor, do as you do in your own example.

    public CustomObject(DataRow row)
    {
        Key = row[0].ToString();
        // And so on...
    }
    

    One other way would be to introduce generics, and make a new function in your SQL-class

    Example (Took code from Passing arguments to C# generic new() of templated type):

    // This function should reside in your SQL-class.
    public IEnumerable<T> ExecuteObject<T>(string sql)
    {
        List<T> items = new List<T>();
        var data = ExecuteDataTable(sql); // You probably need to build a ExecuteDataTable for your SQL-class.
        foreach(var row in data.Rows)
        {
            T item = (T)Activator.CreateInstance(typeof(T), row);
            items.Add(item);
        }
        return items;
    }
    

    Example usage:

    public IEnumerable<CustomObject> GetCustomObjects()
    {
        return SQL.ExecuteObject<CustomObject>("SELECT * FROM CustomObject");
    }
    

    I have tested this code in LinqPad, it should work.

    0 讨论(0)
  • 2021-02-01 21:14

    Assumption: if you need objects only for serialization or simple ad-hoc output.

    You can use ExpandoObject and SqlDataReader.GetSchemaTable() like this:

        private IEnumerable<dynamic> ReaderToAnonymmous(SqlCommand comm) {
            using (var reader = comm.ExecuteReader()) {
                var schemaTable = reader.GetSchemaTable();
    
                List<string> colnames = new List<string>();
                foreach (DataRow row in schemaTable.Rows) {
                    colnames.Add(row["ColumnName"].ToString());
                }
    
                while (reader.Read()) {
                    var data = new ExpandoObject() as IDictionary<string, Object>;
                    foreach (string colname in colnames) {
                        var val = reader[colname];
                        data.Add(colname, Convert.IsDBNull(val) ? null : val);
                    }
    
                    yield return (ExpandoObject)data;
                }
            }
        }
    

    Although there are posted faster solutions (i posted this as alternative lazy approach for ad-hoc SQL/Reader results/outputs).

    0 讨论(0)
提交回复
热议问题