9.动态生成实体类,根据XML模板使用Emit生成动态类绑定到DataGrid

   在实际项目中,我们可能会遇到用户自定义XML模板字段,根据这个模板上的字段来显示相应的字段的值到DataGrid。在这种情况下,需要使用 XmlReader解析获取这个用户自定义的XML模板上有哪些字段,根据这个字段动态的生成一个实体类,之后再为此动态生成的实体类实例化,并且生成实 体类集合绑定到DataGrid即可。(注意:平时我们绑定DataGrid是先在代码里面声明了实体类,实例化多个实体化类,形成实体类集合,绑定到 DataGrid。可如果用户自定义XML格式的字段的时候,每次的实体类就不能为静态的了。必须为动态的才行。)

         一、首先我们准备一个XML格式的模板数据。模拟用户自定义的XML模板字段。这里的XML模板字段可以*添改。

 


  1. <NewDataSet> 
  2.                 <Table  TableName='City'  TableShowName='城市' > 
  3.                     <Column   Name='CityName' ShowName='城市名称' />  
  4.                     <Column   Name='CityTel' ShowName='城市区号' />  
  5.                     <Column   Name='CityCounty' ShowName='城市所属国家' /> 
  6.                 </Table>  
  7.                 <Table  TableName='User'  TableShowName='用户' > 
  8.                     <Column   Name='UserName' ShowName='用户名' />  
  9.                     <Column   Name='UserPwd' ShowName='用户密码' />  
  10.                     <Column   Name='UserTel' ShowName='用户电话' />  
  11.                     <Column   Name='UserEmail' ShowName='用户邮箱' />  
  12.                 </Table>  
  13.             </NewDataSet> 

        再声明一个实体类来保存字段的Name和显示名称ShowName,并且把这些字段存放到List<>中去。

 


  1. /// <summary> 
  2. /// 存放动态表格的字段 
  3. /// </summary> 
  4. public class GridClass 
  5. private string _ShowName; 
  6. private string _Name; 
  7. /// <summary> 
  8. /// 显示名称 
  9. /// </summary> 
  10. public string ShowName 
  11. get { return _ShowName; } 
  12. set { _ShowName = value; } 
  13. /// <summary> 
  14. /// 字段名称 
  15. /// </summary> 
  16. public string Name 
  17. get { return _Name; } 
  18. set { _Name = value; } 

        二、准备好数据之后,我们开始解析这个XML文档,并且根据此XML文档生成两个动态Tabel实体类。这里我们首先贴出关键代码,根据代码来解读:

 


  1. List<GridClass> gridClassList = new List<GridClass>();//声明一个GridClass实体类的集合。 
  2.      using (XmlReader xmlRead = XmlReader.Create(new StringReader(XMLStr))) 
  3.      { 
  4.          xmlRead.Read(); 
  5.          while (xmlRead.Read()) 
  6.          { 
  7.              //获取到一个TABLE,然后转化为一个动态的实体类。 
  8.              gridClassList.Clear();//循环读取Tabel元素的时候,清空GridClass实体类集合。 
  9.              xmlRead.ReadToFollowing("Table");//读取Table的显示名称和Tabel的名称。 
  10.              string TableShowName = xmlRead.GetAttribute("TableShowName"); 
  11.              string TableName = xmlRead.GetAttribute("TableName"); 
  12.              try 
  13.              { 
  14.                  using (XmlReader xReader2 = xmlRead.ReadSubtree())//将此Tabel读取为一个子XmlReader以供下一步使用。 
  15.                  { 
  16.                      while (xReader2.ReadToFollowing("Column")) 
  17.                      { 
  18.                          //循环读取Column元素,然后获取到Tabel的字段的显示名称和字段名称。并且添加到gridClassList 
  19.                          string ShowName = xReader2.GetAttribute("ShowName"); 
  20.                          string Name = xReader2.GetAttribute("Name"); 
  21.                          GridClass gclass = new GridClass() { ShowName = ShowName, Name = Name }; 
  22.                          gridClassList.Add(gclass); 
  23.                      } 
  24.                      //声明一个Dictionary<string, string>的List<>集合 
  25.                      List<Dictionary<string, string>> dicList = new List<Dictionary<string, string>>(); 
  26.                      //声明一个Dictionary<string, string>实体,然后为此实体赋值列名为读取Column元素得到的字段名称 
  27.                      Dictionary<string, string> dic = new Dictionary<string, string>(); 
  28.                      for (int j = 0; j < gridClassList.Count; j++) 
  29.                      { 
  30.                          dic[gridClassList[j].Name] = "--"+gridClassList[j].Name+"--"
  31.                      } 
  32.                      dicList.Add(dic); 
  33.                      //动态生成一个DataGrid,并且绑定数据源 
  34.                      DataGrid dgrid = new DataGrid(); 
  35.                      dgrid.HorizontalAlignment = HorizontalAlignment.Left
  36.                      dgrid.VerticalAlignment = VerticalAlignment.Top
  37.                      dgrid.Margin = new Thickness(20, 5, 0, 0); 
  38.                      dgrid.Width = 960; 
  39.                      dgrid.Name = TableName; 
  40.                      dgrid.ItemsSource = GetEnumerable(dicList).ToDataSource(); 
  41.  
  42.                      this.mainPanel.Children.Add(dgrid); 
  43.                  } 
  44.              } 
  45.              catch (Exception ex) 
  46.              { } 
  47.          } 
  48.      } 
  49.  } 

        这里解析出关键的字段值,然后动态生成DataGrid,并且绑定了数据库。这些操作是不是很熟悉?几乎和原来绑定数据差不多?关键在以下几句:

 


  1. //声明一个Dictionary<string, string>的List<>集合 
  2.                           List<Dictionary<string, string>> dicList = new List<Dictionary<string, string>>(); 
  3.                           //声明一个Dictionary<string, string>实体,然后为此实体赋值列名为读取Column元素得到的字段名称 
  4.                           Dictionary<string, string> dic = new Dictionary<string, string>(); 
  5.                           for (int j = 0; j < gridClassList.Count; j++) 
  6.                           { 
  7.                               dic[gridClassList[j].Name] = "--"+gridClassList[j].Name+"--"
  8.                           } 
  9.                           dicList.Add(dic); 

        通过这段代码我们得到了一个 List<Dictionary<string, string>>格式的Dictionary<string, string>集合。这里Dictionary的Key值,即为一列字段名,Value为该列的具体值,那么一个 Dictionary[0],Dictionary[1],Dictionary[2],Dictionary[3],Dictionary[4],Dictionary[5] 即为一行有字段名的数据,整个List<Dictionary>就是一个多行有字段名的数据,这就相当于一个类似于DataTabel的表 了。

        三、当然更关键的下一句:dgrid.ItemsSource = GetEnumerable(dicList).ToDataSource();这句话肯定是得到了一个类似于List<object>对象 集的东西,才能够绑定到ItemSource属性上来。具体是如何得到这个数据集的呢?在这里暂且先卖一个关子,请看下面源码:

 


  1. public IEnumerable<IDictionary> GetEnumerable(List<Dictionary<string, string>> SourceList) 
  2.      { 
  3.          for (int i = 0; i < SourceList.Count; i++) 
  4.          { 
  5.              var dict = new Dictionary<string, string>(); 
  6.              dict = SourceList[i]; 
  7.              yield return dict; 
  8.          } 
  9.      } 

        这个函数是将List<Dictionary<string, string>>的数据,通过遍历的方式读取出来,使用yield return关键字来获取到IEnumerable<IDictionary>类型的返回值。在这里取到一个数据格式转换的作用,我们看下面 这个隐藏起来的类。这个类是国外友人Vladimir Bodurov 编 写的,它扩展了IEnumerable接口,让此接口可以将普通的IEnumerable集合通过Emit转化成为实体类集合。想必现在我们再来看 dgrid.ItemsSource = GetEnumerable(dicList).ToDataSource();这句代码就很清晰了吧,在这里我们就实现了动态创建类的过程。


  1. DataSourceCreator.cs 
  2.  
  3. public static class DataSourceCreator 
  4. private static readonly Regex PropertNameRegex = 
  5. new Regex(@"^[A-Za-z]+[A-Za-z1-9_]*$", RegexOptions.Singleline); 
  6. public static List<object> ToDataSource(this IEnumerable<IDictionary> list) 
  7. IDictionary firstDict = null
  8. bool hasData = false
  9. foreach (IDictionary currentDict in list) 
  10. hasData = true
  11. firstDict = currentDict; 
  12. break; 
  13. if (!hasData) 
  14. return new List<object> { }; 
  15. if (firstDict == null
  16. throw new ArgumentException("IDictionary entry cannot be null"); 
  17. Type objectType = null
  18. TypeBuilder tb = GetTypeBuilder(list.GetHashCode()); 
  19. ConstructorBuilder constructor = 
  20. tb.DefineDefaultConstructor( 
  21. MethodAttributes.Public | 
  22. MethodAttributes.SpecialName | 
  23. MethodAttributes.RTSpecialName); 
  24. foreach (DictionaryEntry pair in firstDict) 
  25. if (PropertNameRegex.IsMatch(Convert.ToString(pair.Key), 0)) 
  26. CreateProperty(tb, 
  27. Convert.ToString(pair.Key), 
  28. pair.Value == null ? 
  29. typeof(object) : 
  30. pair.Value.GetType()); 
  31. else 
  32. throw new ArgumentException( 
  33. @"Each key of IDictionary must be 
  34. alphanumeric and start with character."); 
  35. objectType = tb.CreateType(); 
  36. return GenerateArray(objectType, list, firstDict); 
  37. private static List<object> GenerateArray(Type objectType, IEnumerable<IDictionary> list, IDictionary firstDict) 
  38. var itemsSource = new List<object>(); 
  39. foreach (var currentDict in list) 
  40. if (currentDict == null
  41. throw new ArgumentException("IDictionary entry cannot be null"); 
  42. object row = Activator.CreateInstance(objectType); 
  43. foreach (DictionaryEntry pair in firstDict) 
  44. if (currentDict.Contains(pair.Key)) 
  45. PropertyInfo property = 
  46. objectType.GetProperty(Convert.ToString(pair.Key)); 
  47. property.SetValue( 
  48. row, 
  49. Convert.ChangeType( 
  50. currentDict[pair.Key], 
  51. property.PropertyType, 
  52. null), 
  53. null); 
  54. itemsSource.Add(row); 
  55. return itemsSource; 
  56. private static TypeBuilder GetTypeBuilder(int code) 
  57. AssemblyName an = new AssemblyName("TempAssembly" + code); 
  58. AssemblyBuilder assemblyBuilder = 
  59. AppDomain.CurrentDomain.DefineDynamicAssembly( 
  60. an, AssemblyBuilderAccess.Run); 
  61. ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule"); 
  62. TypeBuilder tb = moduleBuilder.DefineType("TempType" + code 
  63. , TypeAttributes.Public | 
  64. TypeAttributes.Class | 
  65. TypeAttributes.AutoClass | 
  66. TypeAttributes.AnsiClass | 
  67. TypeAttributes.BeforeFieldInit | 
  68. TypeAttributes.AutoLayout 
  69. , typeof(object)); 
  70. return tb; 
  71. private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType) 
  72. FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, 
  73. propertyType, 
  74. FieldAttributes.Private); 
  75.  
  76. PropertyBuilder propertyBuilder = 
  77. tb.DefineProperty( 
  78. propertyName, PropertyAttributes.HasDefault, propertyType, null); 
  79. MethodBuilder getPropMthdBldr = 
  80. tb.DefineMethod("get_" + propertyName, 
  81. MethodAttributes.Public | 
  82. MethodAttributes.SpecialName | 
  83. MethodAttributes.HideBySig, 
  84. propertyType, Type.EmptyTypes); 
  85. ILGenerator getIL = getPropMthdBldr.GetILGenerator(); 
  86. getIL.Emit(OpCodes.Ldarg_0); 
  87. getIL.Emit(OpCodes.Ldfld, fieldBuilder); 
  88. getIL.Emit(OpCodes.Ret); 
  89. MethodBuilder setPropMthdBldr = 
  90. tb.DefineMethod("set_" + propertyName, 
  91. MethodAttributes.Public | 
  92. MethodAttributes.SpecialName | 
  93. MethodAttributes.HideBySig, 
  94. null, new Type[] { propertyType }); 
  95. ILGenerator setIL = setPropMthdBldr.GetILGenerator(); 
  96. setIL.Emit(OpCodes.Ldarg_0); 
  97. setIL.Emit(OpCodes.Ldarg_1); 
  98. setIL.Emit(OpCodes.Stfld, fieldBuilder); 
  99. setIL.Emit(OpCodes.Ret); 
  100. propertyBuilder.SetGetMethod(getPropMthdBldr); 
  101. propertyBuilder.SetSetMethod(setPropMthdBldr); 

本文实例源码采用VS2010+Silverlight 4.0编写,点击 SLReadDanamicClassToDataGrid.rar 下载此源码。



文转自程兴亮 51CTO博客,原文链接:http://blog.51cto.com/chengxingliang/821464

上一篇:阿里云容器&服务网格产品技术动态(202112-202201)


下一篇:CMake学习记录--list(列表操作命令)