26

現在、私は次のようなものを使用しています:

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

割り当てを手動で行う代わりに、このマッピングを直接行う方法はありますか (列名がフィールド名と一致すると仮定します)。

ただし、1 つの要件は、純粋な LINQ ベースのアプローチを使用するのではなく、SQL クエリを使用する現在のアプローチでこれを行うことです。1 つには、SQL クエリは十分に大きく、複雑な JOIN を含み、徹底的にテストされているため、現時点ではこれ以上バグを導入したくありません。助言がありますか?

4

8 に答える 8

9

要件に合わせて汎用メソッドを作成することで実現できます。また、新しいメソッドをデータ テーブルの拡張として作成することもできます。

    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>();
于 2014-09-23T07:59:25.197 に答える
9

簡単な解決策の 1 つCustomObjectは、a を取るコンストラクターを作成することですDataRow(例から、別のクラスの場合は修正してください)。

そして、新しいコンストラクターで、独自の例と同じように実行します。

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

もう1つの方法は、ジェネリックを導入し、SQLクラスに新しい関数を作成することです

例 (テンプレート化された型の C# ジェネリック 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;
}

使用例:

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

このコードを LinqPad でテストしましたが、動作するはずです。

于 2012-07-03T07:09:20.840 に答える
7

MicroORM を調べる必要があります。使用する必要がある SDL を提供する通常の ORM とは異なり、MicroORM では独自の SQL クエリを使用でき、SQL 結果セットから C# オブジェクトへのマッピングと C# オブジェクトから SQL パラメーターへのマッピングのみを提供します。

私のお気に入りはPetaPoco です。これも、独自の SQL を使用するクエリ ビルダーを提供しますが、パラメーター番号の適切な操作を行います。

于 2014-09-23T09:42:02.927 に答える
4

@ user1553525の答えは素晴らしいですが、列名がプロパティ名と正確に一致しない場合は機能しません。

したがって、最初にカスタム属性を作成する必要があります。次に、逆シリアル化しようとしているクラスの属性を使用します。最後に、DataTable を逆シリアル化します。

カスタム属性

クラス内のプロパティに適用されるカスタム属性を作成します。NameDataTable から正しい列を取得するために後で使用するプロパティを持つクラスを作成します。

[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();
    }            
}
于 2019-08-18T01:31:38.077 に答える
3

前提:シリアル化または単純なアドホック出力のためだけにオブジェクトが必要な場合。

ExpandoObject次のSqlDataReader.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;
            }
        }
    }

より高速なソリューションが投稿されていますが (アドホック SQL/リーダーの結果/出力の代替の遅延アプローチとしてこれを投稿しました)。

于 2016-10-10T14:26:14.297 に答える
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;
    }
于 2021-06-10T02:26:58.653 に答える