如何将sql查询的结果映射到对象?

Leg*_*end 14 c# sql

目前,我使用的是这样的:

    try
    {
      dr = SQL.Execute(sql);

      if(dr != null) {
         while(dr.Read()) {
           CustomObject c = new CustomObject();
           c.Key = dr[0].ToString();
           c.Value = dr[1].ToString();
           c.Meta = dr[2].ToString();
           customerInfo.CustomerList.Add(c);
         }
      }
      else
      {
          customerInfo.ErrorDetails="No records found";
      } 
Run Code Online (Sandbox Code Playgroud)

而不是我手动执行分配,有没有办法直接执行此映射(假设列名称与字段名称匹配).

然而,一个要求是我希望通过我当前使用sql查询的方法而不是使用基于纯LINQ的方法来实现这一点.首先,SQL查询足够大,涉及复杂的JOIN并且已经过彻底测试,因此我不想在此时引入更多错误.有什么建议?

NoL*_*ing 6

一个简单的解决办法是让一个构造你CustomObject是需要DataRow(从例如,所以如果它是另一个类,请指正).

在您的新构造函数中,按照您自己的示例执行操作.

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

另一种方法是引入泛型,并在SQL类中创建一个新函数

示例(从传递参数到模板化类型的C#generic new()的代码):

// 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;
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

public IEnumerable<CustomObject> GetCustomObjects()
{
    return SQL.ExecuteObject<CustomObject>("SELECT * FROM CustomObject");
}
Run Code Online (Sandbox Code Playgroud)

我已经在LinqPad中测试了这段代码,它应该可以工作.


小智 6

您可以通过为您的需求创建通用方法来实现.您还可以将新方法作为数据表的扩展名.

    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;
    }
}
Run Code Online (Sandbox Code Playgroud)

}

用法:

    DataTable dtCustomer = GetCustomers();
    List<CustomObject> CustomObjectList = dtCustomer.ToList<CustomObject>();
Run Code Online (Sandbox Code Playgroud)

  • 我害怕 try, catch {continue;} 的答案会得到赞成票...... (2认同)

Ida*_*rye 6

您应该研究MicroORM。与提供您必须使用的SDL的常规ORM不同,MicroORM允许您使用自己的SQL查询,并且仅提供从SQL结果集到C#对象以及从C#对象到SQL参数的映射。

我最喜欢的是PetaPoco,它还提供了一个查询构建器,该构建器使用您自己的SQL,但是对参数编号进行了一些巧妙的操作。


Sui*_*pps 5

@user1553525的答案很好,但是,如果您的列名称与属性名称不完全匹配,则它不起作用。

因此,首先您需要创建一个自定义属性。然后使用您要反序列化的类中的属性,最后,您要反序列化 DataTable。

自定义属性

我们创建一个自定义属性,该属性将应用于我们类内部的属性。我们创建的类具有属性Name,稍后我们将使用该属性从数据表中获取正确的列。

[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class MySqlColName : Attribute
{
    private string _name = "";
    public string Name { get => _name; set => _name = value; }

    public MySqlColName(string name)
    {
        _name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

要反序列化的类

接下来,在我们要填充的类中,我们将声明将使用[MySqlColName]我们刚刚创建的属性链接到类中的属性的列名称。

但是,如果属性名称与数据库列相同,我们不需要在属性中指定列名称,因为函数.ToList<>()将采用属性名称中的列名称。

public class EventInfo
{
    [MySqlColName("ID")]
    public int EventID { get; set; }

    //Notice there is no attribute on this property? 
    public string Name { get; set; }

    [MySqlColName("State")]
    public string State { get; set; }

    [MySqlColName("Start_Date")]
    public DateTime StartDate { get; set; }

    [MySqlColName("End_Date")]
    public DateTime EndDate { get; set; }

}
Run Code Online (Sandbox Code Playgroud)

DataTable ToList 扩展方法

最后,我们通过添加检查来修改@user1553525的答案,以查看是否已提供我们的自定义属性。如果是,那么我们将列的名称设置为提供的名称,否则,我们使用属性名称(请参阅 try 块内的代码)。

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
                {
                    //Set the column name to be the name of the property
                    string ColumnName = prop.Name;

                    //Get a list of all of the attributes on the property
                    object[] attrs = prop.GetCustomAttributes(true);
                    foreach (object attr in attrs)
                    {
                        //Check if there is a custom property name
                        if (attr is MySqlColName colName)
                        {
                            //If the custom column name is specified overwrite property name
                            if (!colName.Name.IsNullOrWhiteSpace())                                        
                                ColumnName = colName.Name;
                        }
                    }

                    PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);

                    //GET THE COLUMN NAME OFF THE ATTRIBUTE OR THE NAME OF THE PROPERTY
                    propertyInfo.SetValue(obj, Convert.ChangeType(row[ColumnName], propertyInfo.PropertyType), null);
                }
                catch
                {
                    continue;
                }
            }

            list.Add(obj);
        }

        return list;
    }
    catch
    {
        return null;
    }
}//END METHOD
Run Code Online (Sandbox Code Playgroud)

用法

最后,我们可以调用该.ToList<>()方法并获取序列化对象的列表

List<EventInfo> CustomObjectList;

using (DataTable dtCustomer = GetDataTable("SELECT * FROM EventIndex"))
{
    CustomObjectList = dtCustomer.ToList<EventInfo>();
}
Run Code Online (Sandbox Code Playgroud)

旁注:我使用了一些自定义方法

public static bool IsNullOrWhiteSpace(this string x)
{
    return string.IsNullOrWhiteSpace(x);
}

public static DataTable GetDataTable(string Query)
{
    MySqlConnection connection = new MySqlConnection("<Connection_String>");
    try
    {            
        DataTable data = new DataTable();
        connection.Open();
        using (MySqlCommand command = new MySqlCommand(Query, connection))
        {
            data.Load(command.ExecuteReader());
        }
        return data;

    }
    catch (Exception ex)
    {
        // handle exception here
        Console.WriteLine(ex);
        throw ex;
    }
    finally
    {
        connection.Close();
    }            
}
Run Code Online (Sandbox Code Playgroud)