汎用と反射を利用する.データセットのデータをコレクションに再帰的に追加

28584 ワード

データベースにはいくつかの属性があり、C#コードにマッピングされたテーブルstudent_があります.Modelクラス、sql文:「select*from student」(テストのみ).
 1 public class Student_Model
 2     {
 3         public int StudentNo { get; set; }
 4         public string LoginPwd { get; set; }
 5         public string StudentName { get; set; }
 6         public bool Sex { get; set; }
 7         public int GradeID { get; set; }
 8         public string Phone { get; set; }
 9         public string Address { get; set; }
10         public DateTime BornDate { get; set; }
11         public string Email { get; set; }
12         public string IdentityCard { get; set; }
13     }

そしてADOを通過する.Netはデータを読み取り、読み取ったデータをDataSetに格納します.このとき、問題が発生しました.私たちの伝統的な方法は、ループで行ごとに追加することです.例えば、次のようにします.
 1 List students = ...; // Student_Model List 
 2 DataSet ds =...;// ds , 
 3 foreach(DataRow row in ds.Tables[0].Rows)
 4 {
 5     students.Add(
 6         new Student_Model{
 7             StudentNo = item["studentno"].ToInt32(),
 8             LoginPwd = item["LoginPwd"].ToString(),
 9             StudentName = item["StudentName"].ToString(),
10             Sex = item["Sex"].ToBoolean(),
11             GradeID = item["GradeID"].ToInt32(),
12             Phone = item["Phone"].ToString(),
13             Address = item["Address"].ToString(),
14             BornDate = item["BornDate"].ToDateTime(),
15             Email = item["Email"].ToString(),
16             IdentityCard = item["IdentityCard"].ToString(),
17         }
18     );
19 }

このうちToInt 32()などの方法は、他のクラスで書き換えられ、最後に書き換えコードが追加されます.
上の遺伝コードから分かるように、データは集合に正常に追加できますが、抽象的なファクトリを利用して異なるデータベースを呼び出すと、同じ方法で何回も書く必要があります.あるいは、多くのクエリー方法を作成し、ds対象を順にデータセットに挿入する必要があります.あるいは、このオブジェクトには100の属性があります.1000もあります.
この場合、コードをカプセル化して多重化する必要があります.
しかし、私はコピーで貼ることができます.では、このページを閉じることができます.
この方法を抽出するのは良い方法ですが、具体的にはどうすればいいですか.
多重化するコードを一つの方法に抽出する.例えば、前のコードブロックのforeachサイクル全体は、明らかに、これは私たちが大量に多重化することになる.
単純な抽出ではだめです.この場合、汎用、反射、再帰を使用して、オブジェクトに含まれるカスタムオブジェクトなど、すべてのオブジェクトをクエリーできます.例えば、Student_Modelには学年クラスGradeが含まれています.Model.
手順は次のとおりです.
DataSetToListなどの汎用クラスを作成します.
1 public class DataSetToList
2     {
3         
4     }

このクラスにメソッドを追加します.
public List GetList(DataSet ds, int TableIndex)
        {
            List lists = new List();
            foreach (DataRow item in ds.Tables[TableIndex].Rows)
            {
                // 
            }
            return lists;
        }

パッケージと多重化を完了し、コードを次のように変更します.
 1 public List GetList(DataSet ds, int TableIndex)
 2         {
 3             List lists = new List();
 4             foreach (DataRow item in ds.Tables[TableIndex].Rows)
 5             {
 6                 T t = Activator.CreateInstance();// 
 7                 foreach(PropertyInfo p in t.GetType().GetProperties()){
 8                     p.SetValue(t,item[p.Name],null);
 9                 }
10                 lists.Add(t);
11             }
12             return lists;
13         }

以上のコードを分析して、まず1つの集合を作成して、この集合は最後に結果として返されます.次に、パラメータDataSetのdsオブジェクトの行をループします.
ここで注意したいのは、T=Activatorの汎用オブジェクトを作成する方法です.CreateInstance();
Activatvatorクラスについては、MSDNでは、ローカルまたはリモートでオブジェクトモデルを作成するための特定のメソッドが含まれているため、継承できないと解釈する.このクラスの署名はpublic class seald Activatvator:Activator ;ネーミングスペースはSystemです.
ActivatorについてMSDNの記述は、無パラメトリック構造関数を使用して、汎用型パラメータを指定する指定型のインスタンスを作成することである.この言葉は少し言いにくいように聞こえますが、実は現在の汎用クラスのオブジェクトを作成し、作成方法は無パラメトリック関数です.この方法の署名はpublic static T CreateInstance()である.
では、ここまでオブジェクトを作成しましたが、オブジェクトに値を付けていません.この場合、汎用クラスのすべての共通属性を遍歴する必要があります.
汎用クラスの共通属性は、汎用クラスのオブジェクトtのGetType()メソッドによって得るtの現在のインスタンスのTypeであり、MSDNでは、タイプ宣言:クラスタイプ、インタフェースタイプ、配列タイプ、値タイプ、列挙タイプ、タイプパラメータ、汎用タイプ定義、およびオープンまたはクローズド構造の汎用タイプを表す.このTypeクラスの署名はpublic abstract class Type:MemberInfo,Type, IReflect
次に、このタイプのGetProperties()メソッドを呼び出して、すべての共通属性を取得し、配列を返します.p.SetValue(パラメータ1:その属性値のオブジェクト、パラメータ2:新しい属性値、パラメータ3:インデックス化属性のオプションインデックス値).このメソッド署名は、public virtual void SetValue(Object obj,Object value,Object[]index);
これで、「select*from student」の文クエリーなどの簡単なクエリーを完了します.問題はありませんが、Student_Modelクラスは少し修正しますか?
 1 public int StudentNo { get; set; }
 2         public string LoginPwd { get; set; }
 3         public string StudentName { get; set; }
 4         public bool Sex { get; set; }
 5         public int GradeID { get; set; }
 6         public string Phone { get; set; }
 7         public string Address { get; set; }
 8         public DateTime BornDate { get; set; }
 9         public string Email { get; set; }
10         public string IdentityCard { get; set; }
11         // 
12       public Grade_Model Grade { get; set; }
13 }

12行目を追加しました.そのため、Grade_を追加しました.Modelのクラス:
1 public class Grade_Model
2     {
3         public int GradeID { get; set; }        // 
4         public string GradeName { get; set; }   // 
5     }

では、同じように上のいわゆる共通の方法で、ここまで、まだ適用されますか?
結果はシステムです.ArgumentException:列「Grade」はテーブルTable異常に属さない;少し修正しなければなりません
 1 public class DataSetToList
 2     {
 3         public List GetList(DataSet ds, int TableIndex)
 4         {
 5             List lists = new List();
 6             foreach (DataRow item in ds.Tables[TableIndex].Rows)
 7             {
 8                 T t = Activator.CreateInstance();// 
 9                 foreach(PropertyInfo p in t.GetType().GetProperties()){
10                     // 
11                     if (p.PropertyType.ToString().StartsWith("System."))
12                     {
13                         p.SetValue(t, item[p.Name], null);
14                     }
15                     else
16                     {
17                         // , ...
18                     }
19                 }
20                 lists.Add(t);
21             }
22             return lists;
23         }
24     }

私たちはif判断を追加して、彼がシステムタイプの時に正常に値を割り当てて、そうでなければ、新しいコードを書く必要があります.
属性PropertyInfoのタイプはGetType()ではなく、その属性PropertyTypeで取得され、GetType()は現在の属性のオブジェクトインスタンスを取得するTypeである.
明らかに、システムタイプでなければ、Grade_のようにカスタマイズされているに違いありません.Model、ここではGrade_を考慮します.ModelクラスにSubject_のような3つ目のクラスがあるかどうかModelカリキュラム情報クラス;
 1 public List GetList(DataSet ds, int TableIndex)
 2         {
 3             List lists = new List();
 4             foreach (DataRow item in ds.Tables[TableIndex].Rows)
 5             {
 6                 T t = Activator.CreateInstance();// 
 7                 GetT(item,t);
 8                 lists.Add(t);
 9             }
10             return lists;
11         }
12 
13         public void GetT(DataRow row,Object obj)
14         {
15             foreach (PropertyInfo p in obj.GetType().GetProperties())
16             {
17                 if (p.PropertyType.ToString().StartsWith("System."))
18                 {
19                     if(row.Table.Columns.Contains(p.Name)){
20                         p.SetValue(obj,row[p.Name],null);
21                     }
22                 }
23                 else
24                 {
25                     Object o = p.PropertyType.Assembly.CreateInstance(p.PropertyType.ToString());
26                     GetT(row, o);
27                     p.SetValue(obj,o,null);
28                 }
29             }
30         }

これで、このコードは完全になり、基本的に様々なクエリーによるds変換に対応することができます.もちろん、コードはエンティティクラスのプライマリ外部キーマッピングに注意しなければなりません.
上記のコードを分析します.
パラメータがDataSet型とObject型であるGetT()法を抽出した.
19行目rowTable.Columns.Contains(p.Name)は、データベース列に属性名が含まれているかどうかを判断し、もしそうであればデータを追加し、そうでなければ続行を返します.
23行目から、この共通属性がカスタムタイプである場合、属性のオブジェクトからPropertyType属性を取得し、その後、その属性タイプが存在するプログラムセットを取得し、p.PropertyTypeである属性タイプのオブジェクトを作成するObjectを再作成する.ToString();
最後に、ToInt 32()の書き換え方法を説明します.
 1     public static class ToolHelper
 2     {
 3         #region Convert Method
 4         public static Int32 ToInt32(this Object obj)
 5         {
 6             return Convert.ToInt32(obj);
 7         }
 8         public static Int16 ToInt16(this Object obj)
 9         {
10             return Convert.ToInt16(obj);
11         }
12         public static Int64 ToInt64(this Object obj)
13         {
14             return Convert.ToInt64(obj);
15         }
16         public static bool ToBoolean(this Object obj)
17         {
18             return Convert.ToBoolean(obj);
19         }
20         public static DateTime ToDateTime(this Object obj)
21         {
22             return Convert.ToDateTime(obj);
23         }
24         public static double ToDouble(this Object obj)
25         {
26             return Convert.ToDouble(obj);
27         }
28         #endregion
29 }

ここまで、この知識点は基本的に紹介しました...