diff --git a/FreeSql/Extensions/CodeFirstExtensions.cs b/FreeSql/Extensions/CodeFirstExtensions.cs new file mode 100644 index 00000000..818be04c --- /dev/null +++ b/FreeSql/Extensions/CodeFirstExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using FreeSql.DataAnnotations; +using FreeSql.Internal; + +namespace FreeSql.Extensions +{ +#if net40 || NETSTANDARD2_0 +#else + public static class CodeFirstExtensions + { + /// + /// 动态构建Class Type + /// + /// + public static DynamicCompileBuilder DynamicEntity(this ICodeFirst codeFirst, string className, + TableAttribute tableAttribute) + { + return new DynamicCompileBuilder().SetClass(className, tableAttribute); + } + + } +#endif +} \ No newline at end of file diff --git a/FreeSql/Extensions/TypeExtensions.cs b/FreeSql/Extensions/TypeExtensions.cs new file mode 100644 index 00000000..a5b75fd8 --- /dev/null +++ b/FreeSql/Extensions/TypeExtensions.cs @@ -0,0 +1,40 @@ +using FreeSql.Internal; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace DynamicBuilder +{ +#if net40 || NETSTANDARD2_0 +#else + public static class TypeExtensions + { + /// + /// 根据动态构建的Class Type生成实例并进行属性赋值 + /// + /// + /// + /// + public static object CreateDynamicEntityInstance(this Type type, IFreeSql fsql, + Dictionary porpertys) + { + return DynamicCompileBuilder.CreateObjectByTypeByCodeFirst(fsql, type, porpertys); + } + + /// + /// 设置对象属性值 + /// + /// + /// + public static void SetPropertyValue(this Type type, IFreeSql fsql, ref object obj, string propertyName, + object propertyValue) + { + var table = fsql.CodeFirst.GetTableByEntity(obj.GetType()); + table.ColumnsByCs[propertyName].SetValue(obj, propertyValue); + } + } +#endif +} diff --git a/FreeSql/Internal/DynamicCompileBuilder.cs b/FreeSql/Internal/DynamicCompileBuilder.cs new file mode 100644 index 00000000..9dfdd0cf --- /dev/null +++ b/FreeSql/Internal/DynamicCompileBuilder.cs @@ -0,0 +1,270 @@ +using FreeSql.DataAnnotations; +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 FreeSql.Internal.Model; +using System.Text; + +namespace FreeSql.Internal +{ +#if net40 || NETSTANDARD2_0 +#else + public class DynamicCompileBuilder + { + private string _className = string.Empty; + private TableAttribute _tableAttribute = null; + private List _properties = new List(); + + /// + /// 配置Class + /// + /// 类名 + /// 类标记的特性[Table(Name = "xxx")] + /// + public DynamicCompileBuilder SetClass(string className, TableAttribute tableAttribute) + { + _className = className; + _tableAttribute = tableAttribute; + return this; + } + + /// + /// 配置属性 + /// + /// 属性名称 + /// 属性类型 + /// 属性标记的特性-支持多个 + /// + public DynamicCompileBuilder Property(string propertyName, Type propertyType, params Attribute[] attributes) + { + _properties.Add(new DynamicPropertyInfo() + { + PropertyName = propertyName, + PropertyType = propertyType, + Attributes = attributes + }); + return this; + } + + private void SetTableAttribute(ref TypeBuilder typeBuilder) + { + var classCtorInfo = typeof(TableAttribute).GetConstructor(new Type[] { }); + var propertyInfos = typeof(TableAttribute).GetProperties().Where(p => p.CanWrite == true).ToArray(); + if (_tableAttribute == null) + { + return; + } + + var propertyValues = new ArrayList(); + foreach (var propertyInfo in _tableAttribute.GetType().GetProperties().Where(p => p.CanWrite == true)) + { + propertyValues.Add(propertyInfo.GetValue(_tableAttribute)); + } + + 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) + { + var propertyName = pinfo.PropertyName; + var propertyType = pinfo?.PropertyType ?? typeof(object); + //设置字段 + 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 propertyValues = new ArrayList(); + foreach (var propertyInfo in tAttribute.GetType().GetProperties().Where(p => p.CanWrite == true)) + { + propertyValues.Add(propertyInfo.GetValue(tAttribute)); + } + + var propertyInfos = tAttribute.GetType().GetProperties().Where(p => p.CanWrite == true).ToArray(); + var constructor = tAttribute.GetType().GetConstructor(new Type[] { }); + 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); + + //设置TableAttribute + SetTableAttribute(ref typeBuilder); + + //设置属性 + SetPropertys(ref typeBuilder); + + //创建类的Type对象 + return typeBuilder.CreateType(); + } + + //委托缓存 + private static ConcurrentDictionary + _delegateCache = new ConcurrentDictionary(); + + //设置动态对象的属性值 使用FreeSql自带功能 + public static object CreateObjectByTypeByCodeFirst(IFreeSql fsql, Type type, + Dictionary porpertys) + { + if (type == null) + return null; + object istance = Activator.CreateInstance(type); + if (istance == null) + return null; + var table = fsql.CodeFirst.GetTableByEntity(type); + foreach (var kv in porpertys) + { + table.ColumnsByCs[kv.Key].SetValue(istance, kv.Value); + } + + return istance; + } + + ////设置动态对象的属性值,使用表达式目录树 + //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 cacheKeyStr = string.Join("-", porpertys.Keys.OrderBy(s => s)); + // var cacheKey = Md5Encryption(cacheKeyStr); + // 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; + //} + + /// + /// 首字母小写 + /// + /// + /// + 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; + } + } +#endif + internal 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