使用Emit反射建立运行时实体模型
做这个功能目的是对旧项目代码分析管理,代码规划。
如果重头开发,统一一个框架小框架搞,人、时间到位都不是问题。但是旧应用系统优化,项目更新,就几个人开发加维护,接口层越多,bll层越来越厚,人来人往留下一堆代码,怎么管理呢?我现在方法是:看,仔细看,再仔细看,改,仔细改,再仔细改。一般出问题了,结合数据库,基本可以解决。如果重新搞,还得熟悉原来的代码,再搬进新的项目,费时费力。有没有一种方式最好不用写代码,做个配置界面,将业务SQL和逻辑用工作流的方式以字符串(JSon、描述对象模型(这玩意我自定义了一个对象来描述各类关系,下次讲))的形式先保存起来,形成一个运行时编程环境,配置完善后通过字符串生成自己喜欢的框架呢?找了好久没找到,就自己写个。
要解决不要写代码,那么所有层的Model(实体、DTO、其他业务模型)肯定要用运行时模型,传输统一用字符串,哪一层都能接受。这样一想,思路就有了。这里的所有模型只做三个事情:实例化、输入和输出。输入为object或者json,输出为。代码做了简单的封装。直接上代码:
动态生成帮助类:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Reflection.Emit; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace MagicModel 10 { 11 public abstract class EmitHelper 12 { 13 14 private static AssemblyName assmblyname; 15 private static string DllName; 16 private static AssemblyBuilder assemblybuilder; 17 private static ModuleBuilder modulebuilder; 18 private static TypeBuilder typebuilder; 19 private static Type _dymaticType; 20 21 public static Type DymaticType 22 { 23 get 24 { 25 return _dymaticType; 26 } 27 28 //set 29 //{ 30 // dymaticType = value; 31 //} 32 } 33 34 public static void Create(string dllname) 35 { 36 DllName = dllname; 37 assmblyname = new AssemblyName(DllName); 38 ///2程序集生成器 39 assemblybuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assmblyname, AssemblyBuilderAccess.RunAndSave); 40 41 // For a single-module assembly, the module name is usually 42 // the assembly name plus an extension. 43 ////3动态创建模块 44 modulebuilder = assemblybuilder.DefineDynamicModule(assmblyname.Name, assmblyname.Name + ".dll"); 45 } 46 public static void CreateClass(string NsClassName) 47 { 48 typebuilder = modulebuilder.DefineType(NsClassName, TypeAttributes.Public); 49 } 50 public static void CreateMember(string MemberName, Type memberType) 51 { 52 FieldBuilder fbNumber = typebuilder.DefineField( 53 "m_" + MemberName, 54 memberType, 55 FieldAttributes.Private); 56 57 58 PropertyBuilder pbNumber = typebuilder.DefineProperty( 59 MemberName, 60 System.Reflection.PropertyAttributes.HasDefault, 61 memberType, 62 null); 63 64 65 MethodAttributes getSetAttr = MethodAttributes.Public | 66 MethodAttributes.SpecialName | MethodAttributes.HideBySig; 67 68 69 MethodBuilder mbNumberGetAccessor = typebuilder.DefineMethod( 70 "get_" + MemberName, 71 getSetAttr, 72 memberType, 73 Type.EmptyTypes); 74 75 ILGenerator numberGetIL = mbNumberGetAccessor.GetILGenerator(); 76 77 numberGetIL.Emit(OpCodes.Ldarg_0); 78 numberGetIL.Emit(OpCodes.Ldfld, fbNumber); 79 numberGetIL.Emit(OpCodes.Ret); 80 81 // Define the "set" accessor method for Number, which has no return 82 // type and takes one argument of type int (Int32). 83 MethodBuilder mbNumberSetAccessor = typebuilder.DefineMethod( 84 "set_" + MemberName, 85 getSetAttr, 86 null, 87 new Type[] { memberType }); 88 89 ILGenerator numberSetIL = mbNumberSetAccessor.GetILGenerator(); 90 // Load the instance and then the numeric argument, then store the 91 // argument in the field. 92 numberSetIL.Emit(OpCodes.Ldarg_0); 93 numberSetIL.Emit(OpCodes.Ldarg_1); 94 numberSetIL.Emit(OpCodes.Stfld, fbNumber); 95 numberSetIL.Emit(OpCodes.Ret); 96 97 // Last, map the "get" and "set" accessor methods to the 98 // PropertyBuilder. The property is now complete. 99 pbNumber.SetGetMethod(mbNumberGetAccessor); 100 pbNumber.SetSetMethod(mbNumberSetAccessor); 101 ///最重要的是你最后要创建类型 102 103 } 104 public static Type SaveClass() 105 { 106 _dymaticType = typebuilder.CreateType(); 107 return DymaticType; 108 } 109 public static void Save() 110 { 111 assemblybuilder.Save(assmblyname.Name + ".dll"); 112 } 113 ///// <summary> 114 ///// 创建一个实体类并保存生成类型 115 ///// </summary> 116 ///// <param name="NsClassName"></param> 117 ///// <param name="propertys"></param> 118 //public void Execute(string NsClassName, Dictionary<string, Type> propertys) 119 //{ 120 121 // CreateClass(NsClassName); 122 // foreach (var item in propertys) 123 // { 124 // CreateMember(item.Key, item.Value); 125 // } 126 // SaveClass(); 127 // Save(); 128 //} 129 130 //public void Execute(List<M_DefineClass> _classes) { 131 132 // foreach (M_DefineClass _class in _classes) 133 // { 134 // CreateClass(_class.NsClassName); 135 // foreach (var prop in _class.Props) 136 // { 137 // CreateMember(prop.MemberName, prop.MemberType); 138 // } 139 // SaveClass(); 140 // } 141 142 //} 143 144 //public void Test(string dllname, string NsClassName, string MemberName, Type memberType) 145 //{ 146 // //1设置程序集名称 147 // assmblyname = new AssemblyName(dllname); 148 // ///2程序集生成器 149 // assemblybuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assmblyname, AssemblyBuilderAccess.RunAndSave); 150 // ////3动态创建模块 151 // modulebuilder = assemblybuilder.DefineDynamicModule(assmblyname.Name, assmblyname.Name + ".dll"); 152 // ///4.创建类 153 // typebuilder = modulebuilder.DefineType(NsClassName, TypeAttributes.Public); 154 // ///5.创建私有字段 155 // FieldBuilder fbNumber = typebuilder.DefineField( 156 // "m_" + MemberName, 157 // memberType, 158 // FieldAttributes.Private); 159 160 // ///6.创建共有属性 161 // PropertyBuilder pbNumber = typebuilder.DefineProperty( 162 // MemberName, 163 // System.Reflection.PropertyAttributes.HasDefault, 164 // memberType, 165 // null); 166 167 168 // MethodAttributes getSetAttr = MethodAttributes.Public | 169 // MethodAttributes.SpecialName | MethodAttributes.HideBySig; 170 171 172 // MethodBuilder mbNumberGetAccessor = typebuilder.DefineMethod( 173 // "get_" + MemberName, 174 // getSetAttr, 175 // memberType, 176 // Type.EmptyTypes); 177 178 // ILGenerator numberGetIL = mbNumberGetAccessor.GetILGenerator(); 179 180 // numberGetIL.Emit(OpCodes.Ldarg_0); 181 // numberGetIL.Emit(OpCodes.Ldfld, fbNumber); 182 // numberGetIL.Emit(OpCodes.Ret); 183 184 // // Define the "set" accessor method for Number, which has no return 185 // // type and takes one argument of type int (Int32). 186 // MethodBuilder mbNumberSetAccessor = typebuilder.DefineMethod( 187 // "set_" + MemberName, 188 // getSetAttr, 189 // null, 190 // new Type[] { typeof(int) }); 191 192 // ILGenerator numberSetIL = mbNumberSetAccessor.GetILGenerator(); 193 // // Load the instance and then the numeric argument, then store the 194 // // argument in the field. 195 // numberSetIL.Emit(OpCodes.Ldarg_0); 196 // numberSetIL.Emit(OpCodes.Ldarg_1); 197 // numberSetIL.Emit(OpCodes.Stfld, fbNumber); 198 // numberSetIL.Emit(OpCodes.Ret); 199 200 // // Last, map the "get" and "set" accessor methods to the 201 // // PropertyBuilder. The property is now complete. 202 // pbNumber.SetGetMethod(mbNumberGetAccessor); 203 // pbNumber.SetSetMethod(mbNumberSetAccessor); 204 205 // ///最重要的是你最后要创建类型 206 // Type t = typebuilder.CreateType(); 207 // assemblybuilder.Save(assmblyname.Name + ".dll"); 208 209 //} 210 211 212 } 213 }
初始动态模型:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MagicModel.BLL { public class DynamicInitBLL { static M_Base _m_base; /// <summary> /// 3.设置和获取,生成结果放入Base里 /// </summary> public static M_Base M_Base { get { return _m_base; } set { _m_base = value; } } /// <summary> /// 初始化,实现步骤为三步 /// </summary> /// <param name="m_base"></param> public DynamicInitBLL(M_Base m_base) { _m_base = m_base; EmitHelper.Create(_m_base._AssamblyName); } /// <summary> /// 1创建单个Class /// </summary> /// <param name="_class"></param> public void Exequte(M_DefineClass _class) { EmitHelper.CreateClass(_class.NsClassName); foreach (var prop in _class.Props) { EmitHelper.CreateMember(prop.MemberName, prop.MemberType); } _m_base.DymaticType.Add(new M_DymaticType() {TypeName=_class.NsClassName,DType= EmitHelper.SaveClass() }); } /// <summary> /// 1创建多个Class /// </summary> /// <param name="_classes"></param> public void Exequte(List<M_DefineClass> _classes) { foreach (var item in _classes) { Exequte(item); } } /// <summary> /// 2保存 /// </summary> public void SaveAssembly() { EmitHelper.Save(); } } }
模型赋值:
using MagicModel; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Reflection; namespace MagicModel.BLL { public class DynamicEmulateBLL { M_Base _m_base; public DynamicEmulateBLL(M_Base _mbase) { _m_base = _mbase; } public object ObjectEmulate(string nsclassName,object data) { Type _classType = _m_base.DymaticType.Where(a => a.TypeName == nsclassName).FirstOrDefault().DType; var obj = Activator.CreateInstance(_classType); foreach (PropertyInfo pi in _classType.GetProperties()) { if (data.GetType().GetProperty(pi.Name)!=null) { Console.WriteLine("有相同的值"); pi.SetValue(obj, data.GetType().GetProperty(pi.Name).GetValue(data)); } } return obj; } public object JsonEmulate(string nsclassName, string jsonstring) { Type _classType = _m_base.DymaticType.Where(a => a.TypeName == nsclassName).FirstOrDefault().DType; return Newtonsoft.Json.JsonConvert.DeserializeObject(jsonstring, _classType); } } }
交互模型:
using System; using System.Collections.Generic; namespace MagicModel { public class M_Base { public string _AssamblyName { get; set; } public List<M_DymaticType> DymaticType { get; set; } = new List<M_DymaticType>(); } public class M_DymaticType { public string TypeName { get; set; } public Type DType { get; set; } } public class M_DefineClass { public string NsClassName { get; set; } public IEnumerable<M_ClassMember> Props { get; set; } } public class M_ClassMember { public string MemberName { get; set; } public Type MemberType { get; set; } } }
下面来测试下效果,先定义10个类:
打开vs里面的cmd开发人员命令提示”,输入ildasm,然后把生成的dll拖进去,有了。
赋值检验:
有效果了
到了这一步,动态模型生成完成,它的调用位置不仅限于运行时代码,也可以在其他环境中使用了。
疑问:
1.运行时环境下处理引用问题是个麻烦事,所以干脆静态一个AssemblyName,项目间交叉引用该怎么解决?
2.我开始用Assembly去取dll,发现两个问题。1,交叉引用出问题。2,赋值取值出问题。
3.这个RunAndSave参数直接加载进来了,所以就在这上面static保存在内存等待以后调用,有没有更好的方法调用?或者说我生成的dll文件想什么时候用什么时候用,在哪里用都由我决定。
以上资料全参考msdn。
模型层可以存库,做生成对比,做db对比等等,后面再优化!