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

时间:2012-06-30 02:38:23

标签: 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";
      } 

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

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

8 个答案:

答案 0 :(得分:6)

一个简单的解决方案是为CustomObject制作一个DataRow的构造函数(来自示例,如果它是另一个类,请纠正我)。

在你的新构造函数中,按照你自己的例子做。

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

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

示例(从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;
}

使用示例:

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

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

答案 1 :(得分:5)

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

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

}

用法:

    DataTable dtCustomer = GetCustomers();
    List<CustomObject> CustomObjectList = dtCustomer.ToList<CustomObject>();

答案 2 :(得分:4)

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

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

答案 3 :(得分:3)

假设:如果您只需要对象进行序列化或简单的临时输出。

您可以像这样使用ExpandoObjectSqlDataReader.GetSchemaTable()

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

虽然发布了更快的解决方案(我将其作为ad-hoc SQL / Reader结果/输出的替代惰性方法发布)。

答案 4 :(得分:1)

以下函数接受SQL字符串和对象,它要求对象具有select语句中每列的属性。必须实例化对象。

  //Have your setup(Eclipse,Android SDK, ioswebkit proxy, Appium) installed in MAC for instance 10.10
    //create an instance with your thread to generalize for both android and ios


    public LaunchAppiumServer(){

        public synchronized void startiOSWebKitProxy(String UDID,String debugProxy,String platform){
                if(Settings.getProperty("testbed").equals("localbrowser") && !Settings.getProperty("LOCAL_SETTINGS").contains("Emulator")){         
                    if(platform.equals("ios")){
                        //Webkit Proxy command          
                        CommandLine iOSProxyCommand = new CommandLine("ios_webkit_debug_proxy");
                        iOSProxyCommand.addArgument("-c");
                        iOSProxyCommand.addArgument(UDID+":"+debugProxy);
                        iOSProxyCommand.addArgument("-F");

                        DefaultExecuteResultHandler iOSProxyresultHandler = new DefaultExecuteResultHandler();
                        DefaultExecutor iOSProxyexecutor = new DefaultExecutor();
                        iOSProxyexecutor.setExitValue(1);
                        try {
                            iOSProxyexecutor.execute(iOSProxyCommand, iOSProxyresultHandler);
                            Thread.sleep(5000);
                            System.out.println("iOS Proxy started in :"+ deviceName);
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }  

         public synchronized void startAppiumServer(String UDID,String port,String  platform,String debugProxy) throws Exception{
              CommandLine command = new CommandLine("appium");
                    command.addArgument("--udid");  
                    command.addArgument(UDID);
                    command.addArgument("--address", false);
                    command.addArgument("127.0.0.1");
                    command.addArgument("--port", false);
                    command.addArgument(port);
                    command.addArgument("--webkit-debug-proxy-port");
                    command.addArgument(debugProxy);
                    command.addArgument("--tmp");
                    command.addArgument("/tmp/appium-"+port);
                    command.addArgument("--full-reset", true);
                    command.addArgument("--log-level", false);
                    command.addArgument("error");
                    command.addArgument("--log", false);
                    Timestamp currentTimestamp = new java.sql.Timestamp(Calendar.getInstance().getTime().getTime());
                    command.addArgument("/Users/sethu/Documents/appiumLogs/appium"+currentTimestamp.toString().replace(" ", "")+".log");
                    DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
                    executor.setExitValue(1);
                    try {
                        executor.execute(command, resultHandler);
                    } catch (IOException e) {
                        System.out.println("Unable to Launch Appium Server: ");
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        System.out.println("Unable to Launch Appium Server: ");
                        e.printStackTrace();
                    }
        }
    }

答案 5 :(得分:1)

@user1553525的答案很好,但是,如果您的列名与属性名不完全匹配,则无法使用。

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

自定义属性

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

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

要反序列化的类

接下来,在我们要填充的类中,我们将使用刚刚创建的属性[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; }

}

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

用法

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

List<EventInfo> CustomObjectList;

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

侧面说明: :我有一些自定义方法

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

答案 6 :(得分:0)

搜索此答案时,我发现您可以使用Dapper库:https://dapper-tutorial.net/knowledge-base/44980945/querying-into-a-complex-object-with-dapper

您可以使用以下内容:

        using (var connection = new SqlConnection(ConnectionString))
        {
            connection.Open();
            IList<CustomObject> result = connection.Query<CustomObject>(sql, commandType: CommandType.Text).ToList();
        }

答案 7 :(得分:0)

虽然这个问题一直存在,但我找不到一个干净的解决方案。为了我的目的,我想出了以下在我的情况下效果很好的方法。

    using System.Dynamic;

    private IEnumerable<ExpandoObject> GetQueryToList()
    {
        try
        {
            using (var conn = new SqlConnection(ConnectionString))
            using (var cmd = new SqlCommand(MyQuery, conn))
            {
                var list = new List<ExpandoObject>();
                conn.Open();
                var reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    var expandoObject = new ExpandoObject();
                    for (var i = 0; i < reader.FieldCount; i++)
                    {
                        ((IDictionary<string, object>) expandoObject).Add(
                            reader.GetName(i), reader[i]);
                    }
                    list.Add(expandoObject);
                }

                reader.Close();
                return list;
            }
        }
        catch (Exception ex)
        {
            var m = MethodBase.GetCurrentMethod();
            Console.WriteLine(ex.Message + " " + m.Name);
        }

        return null;
    }