From b17c2ef4a5bfe6bd44fe8eacd2595212df0f0072 Mon Sep 17 00:00:00 2001 From: d4ilys <963922242@qq.com> Date: Fri, 5 May 2023 10:53:28 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0DynamicEntity=E7=AB=8Bnug?= =?UTF-8?q?et=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DynamicCompileBuilder.cs | 231 ++++++++++++++++++ .../DynamicCompileHelper.cs | 74 ++++++ .../FreeSql.Extensions.DynamicEntity.csproj | 15 ++ .../FreeSql.Extensions.DynamicEntity.xml | 81 ++++++ 4 files changed, 401 insertions(+) create mode 100644 Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs create mode 100644 Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileHelper.cs create mode 100644 Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.csproj create mode 100644 Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.xml diff --git a/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs new file mode 100644 index 00000000..cbea309a --- /dev/null +++ b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Security.Cryptography; +using System.Text; + +namespace FreeSql +{ + /// + /// 动态创建实体类型 + /// + public class DynamicCompileBuilder + { + private string _className = string.Empty; + private Attribute[] _tableAttributes = null; + private List _properties = new List(); + private Type _superClass = null; + + /// + /// 配置Class + /// + /// 类名 + /// 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] + /// + public DynamicCompileBuilder Class(string className, params Attribute[] attributes) + { + _className = className; + _tableAttributes = attributes; + return this; + } + + /// + /// 配置属性 + /// + /// 属性名称 + /// 属性类型 + /// 属性标记的特性-支持多个 + /// + public DynamicCompileBuilder Property(string propertyName, Type propertyType, params Attribute[] attributes) + { + _properties.Add(new DynamicPropertyInfo() + { + PropertyName = propertyName, + PropertyType = propertyType, + Attributes = attributes + }); + return this; + } + + /// + /// 配置父类 + /// + /// 父类类型 + /// + public DynamicCompileBuilder SuperClass(Type superClass) + { + _superClass = superClass; + return this; + } + + private void SetTableAttribute(ref TypeBuilder typeBuilder) + { + if (_tableAttributes == null) return; + + foreach (var tableAttribute in _tableAttributes) + { + var propertyValues = new ArrayList(); + + if (tableAttribute == null) continue; + + var classCtorInfo = tableAttribute.GetType().GetConstructor(new Type[] { }); + + var propertyInfos = tableAttribute.GetType().GetProperties().Where(p => p.CanWrite == true).ToArray(); + + foreach (var propertyInfo in propertyInfos) + propertyValues.Add(propertyInfo.GetValue(tableAttribute)); + + //可能存在有参构造 + if (classCtorInfo == null) + { + var constructorTypes = propertyInfos.Select(p => p.PropertyType); + classCtorInfo = tableAttribute.GetType().GetConstructor(constructorTypes.ToArray()); + var customAttributeBuilder = new CustomAttributeBuilder(classCtorInfo, propertyValues.ToArray()); + typeBuilder.SetCustomAttribute(customAttributeBuilder); + } + else + { + var customAttributeBuilder = new CustomAttributeBuilder(classCtorInfo, new object[0], propertyInfos, + propertyValues.ToArray()); + typeBuilder.SetCustomAttribute(customAttributeBuilder); + } + } + } + + private void SetPropertys(ref TypeBuilder typeBuilder) + { + foreach (var pinfo in _properties) + { + if (pinfo == null) + continue; + var propertyName = pinfo.PropertyName; + var propertyType = pinfo.PropertyType; + //设置字段 + var field = typeBuilder.DefineField($"_{FirstCharToLower(propertyName)}", propertyType, + FieldAttributes.Private); + var firstCharToUpper = FirstCharToUpper(propertyName); + //设置属性方法 + var methodGet = typeBuilder.DefineMethod($"Get{firstCharToUpper}", MethodAttributes.Public, + propertyType, null); + var methodSet = typeBuilder.DefineMethod($"Set{firstCharToUpper}", MethodAttributes.Public, null, + new Type[] { propertyType }); + + var ilOfGet = methodGet.GetILGenerator(); + ilOfGet.Emit(OpCodes.Ldarg_0); + ilOfGet.Emit(OpCodes.Ldfld, field); + ilOfGet.Emit(OpCodes.Ret); + + var ilOfSet = methodSet.GetILGenerator(); + ilOfSet.Emit(OpCodes.Ldarg_0); + ilOfSet.Emit(OpCodes.Ldarg_1); + ilOfSet.Emit(OpCodes.Stfld, field); + ilOfSet.Emit(OpCodes.Ret); + + //设置属性 + var propertyBuilder = + typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null); + propertyBuilder.SetGetMethod(methodGet); + propertyBuilder.SetSetMethod(methodSet); + + foreach (var pinfoAttribute in pinfo.Attributes) + { + //设置特性 + SetPropertyAttribute(ref propertyBuilder, pinfoAttribute); + } + } + } + + private void SetPropertyAttribute(ref PropertyBuilder propertyBuilder, T tAttribute) + { + if (tAttribute == null) return; + + var propertyInfos = tAttribute.GetType().GetProperties().Where(p => p.CanWrite == true).ToArray(); + var constructor = tAttribute.GetType().GetConstructor(new Type[] { }); + var propertyValues = new ArrayList(); + foreach (var propertyInfo in propertyInfos) + propertyValues.Add(propertyInfo.GetValue(tAttribute)); + + var customAttributeBuilder = + new CustomAttributeBuilder(constructor, new object[0], propertyInfos, propertyValues.ToArray()); + propertyBuilder.SetCustomAttribute(customAttributeBuilder); + } + + /// + /// Emit动态创建出Class - Type + /// + /// + public Type Build() + { + //初始化AssemblyName的一个实例 + var assemblyName = new AssemblyName("FreeSql.DynamicCompileBuilder"); + //设置程序集的名称 + var defineDynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + //动态在程序集内创建一个模块 + var defineDynamicModule = + defineDynamicAssembly.DefineDynamicModule("FreeSql.DynamicCompileBuilder.Dynamics"); + //动态的在模块内创建一个类 + var typeBuilder = + defineDynamicModule.DefineType(_className, TypeAttributes.Public | TypeAttributes.Class, _superClass); + + //设置TableAttribute + SetTableAttribute(ref typeBuilder); + + //设置属性 + SetPropertys(ref typeBuilder); + + //创建类的Type对象 + return typeBuilder.CreateType(); + } + + /// + /// 首字母小写 + /// + /// + /// + private string FirstCharToLower(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + string str = input.First().ToString().ToLower() + input.Substring(1); + return str; + } + + /// + /// 首字母大写 + /// + /// + /// + private string FirstCharToUpper(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + string str = input.First().ToString().ToUpper() + input.Substring(1); + return str; + } + + private static string Md5Encryption(string inputStr) + { + var result = string.Empty; + //32位大写 + using (var md5 = MD5.Create()) + { + var resultBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(inputStr)); + result = BitConverter.ToString(resultBytes); + } + + return result; + } + + class DynamicPropertyInfo + { + public string PropertyName { get; set; } = string.Empty; + public Type PropertyType { get; set; } + public Attribute[] Attributes { get; set; } + } + } +} \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileHelper.cs b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileHelper.cs new file mode 100644 index 00000000..5e839e06 --- /dev/null +++ b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileHelper.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace FreeSql.Extensions.DynamicEntity +{ + /// + /// 动态创建对象帮助类 + /// + public class DynamicCompileHelper + { + /// + /// 动态构建Class - Type + /// + /// + public static DynamicCompileBuilder DynamicBuilder() + { + return new DynamicCompileBuilder(); + } + + /// + /// 委托缓存 + /// + private static readonly ConcurrentDictionary + DelegateCache = new ConcurrentDictionary(); + + /// + /// 设置动态对象的属性值 + /// + /// + /// + /// + public static object CreateObjectByType(Type type, Dictionary porpertys) + { + if (type == null) + return null; + object istance = Activator.CreateInstance(type); + if (istance == null) + return null; + //根据key确定缓存 + var cacheKey = $"{type.GetHashCode()}{porpertys.GetHashCode()}"; + var dynamicDelegate = DelegateCache.GetOrAdd(cacheKey, key => + { + //表达式目录树构建委托 + var typeParam = Expression.Parameter(type); + var dicParamType = typeof(Dictionary); + var dicParam = Expression.Parameter(dicParamType); + var exps = new List(); + var tempRef = Expression.Variable(typeof(object)); + foreach (var pinfo in porpertys) + { + var propertyInfo = type.GetProperty(pinfo.Key); + if (propertyInfo == null) + continue; + var propertyName = Expression.Constant(pinfo.Key, typeof(string)); + exps.Add(Expression.Call(dicParam, dicParamType.GetMethod("TryGetValue"), propertyName, tempRef)); + exps.Add(Expression.Assign(Expression.MakeMemberAccess(typeParam, propertyInfo), + Expression.Convert(tempRef, propertyInfo.PropertyType))); + exps.Add(Expression.Assign(tempRef, Expression.Default(typeof(object)))); + } + + var returnTarget = Expression.Label(type); + exps.Add(Expression.Return(returnTarget, typeParam)); + exps.Add(Expression.Label(returnTarget, Expression.Default(type))); + var block = Expression.Block(new[] { tempRef }, exps); + var @delegate = Expression.Lambda(block, typeParam, dicParam).Compile(); + return @delegate; + }); + var dynamicInvoke = dynamicDelegate.DynamicInvoke(istance, porpertys); + return dynamicInvoke; + } + } +} \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.csproj b/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.csproj new file mode 100644 index 00000000..c1b359b8 --- /dev/null +++ b/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.1;net451;net45; + True + FreeSql.Extensions.DynamicEntity.xml + + + + + Always + + + + diff --git a/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.xml b/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.xml new file mode 100644 index 00000000..b5032f3a --- /dev/null +++ b/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.xml @@ -0,0 +1,81 @@ + + + + FreeSql.Extensions.DynamicEntity + + + + + 动态创建实体类型 + + + + + 配置Class + + 类名 + 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] + + + + + 配置属性 + + 属性名称 + 属性类型 + 属性标记的特性-支持多个 + + + + + 配置父类 + + 父类类型 + + + + + Emit动态创建出Class - Type + + + + + + 首字母小写 + + + + + + + 首字母大写 + + + + + + + 动态创建对象帮助类 + + + + + 动态构建Class - Type + + + + + + 委托缓存 + + + + + 设置动态对象的属性值 + + + + + + +