diff --git a/FreeSql/Internal/DynamicCompileBuilder.cs b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs
similarity index 61%
rename from FreeSql/Internal/DynamicCompileBuilder.cs
rename to Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs
index fb4903de..be1bbf0e 100644
--- a/FreeSql/Internal/DynamicCompileBuilder.cs
+++ b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileBuilder.cs
@@ -1,36 +1,35 @@
-using FreeSql.DataAnnotations;
-using System;
+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
+namespace FreeSql.Extensions.DynamicEntity
{
-#if net40 || NETSTANDARD2_0
-#else
+ ///
+ /// 动态创建实体类型
+ ///
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")]
+ /// 类标记的特性[Table(Name = "xxx")] [Index(xxxx)]
///
- public DynamicCompileBuilder(string className, params Attribute[] attributes)
+ public DynamicCompileBuilder Class(string className, params Attribute[] attributes)
{
_className = className;
_tableAttributes = attributes;
+ return this;
}
///
@@ -51,22 +50,48 @@ namespace FreeSql.Internal
return this;
}
+ ///
+ /// 配置父类
+ ///
+ /// 父类类型
+ ///
+ public DynamicCompileBuilder Extend(Type superClass)
+ {
+ _superClass = superClass;
+ return this;
+ }
+
private void SetTableAttribute(ref TypeBuilder typeBuilder)
{
if (_tableAttributes == null) return;
- var propertyValues = new ArrayList();
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));
-
- var customAttributeBuilder = new CustomAttributeBuilder(classCtorInfo, new object[0], propertyInfos, propertyValues.ToArray());
- typeBuilder.SetCustomAttribute(customAttributeBuilder);
+
+ //可能存在有参构造
+ 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);
+ }
}
}
@@ -74,8 +99,10 @@ namespace FreeSql.Internal
{
foreach (var pinfo in _properties)
{
+ if (pinfo == null)
+ continue;
var propertyName = pinfo.PropertyName;
- var propertyType = pinfo?.PropertyType ?? typeof(object);
+ var propertyType = pinfo.PropertyType;
//设置字段
var field = typeBuilder.DefineField($"_{FirstCharToLower(propertyName)}", propertyType,
FieldAttributes.Private);
@@ -98,7 +125,8 @@ namespace FreeSql.Internal
ilOfSet.Emit(OpCodes.Ret);
//设置属性
- var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null);
+ var propertyBuilder =
+ typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null);
propertyBuilder.SetGetMethod(methodGet);
propertyBuilder.SetSetMethod(methodSet);
@@ -120,7 +148,8 @@ namespace FreeSql.Internal
foreach (var propertyInfo in propertyInfos)
propertyValues.Add(propertyInfo.GetValue(tAttribute));
- var customAttributeBuilder = new CustomAttributeBuilder(constructor, new object[0], propertyInfos, propertyValues.ToArray());
+ var customAttributeBuilder =
+ new CustomAttributeBuilder(constructor, new object[0], propertyInfos, propertyValues.ToArray());
propertyBuilder.SetCustomAttribute(customAttributeBuilder);
}
@@ -138,7 +167,8 @@ namespace FreeSql.Internal
var defineDynamicModule =
defineDynamicAssembly.DefineDynamicModule("FreeSql.DynamicCompileBuilder.Dynamics");
//动态的在模块内创建一个类
- var typeBuilder = defineDynamicModule.DefineType(_className, TypeAttributes.Public | TypeAttributes.Class);
+ var typeBuilder =
+ defineDynamicModule.DefineType(_className, TypeAttributes.Public | TypeAttributes.Class, _superClass);
//设置TableAttribute
SetTableAttribute(ref typeBuilder);
@@ -150,70 +180,6 @@ namespace FreeSql.Internal
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;
- //}
-
///
/// 首字母小写
///
@@ -260,5 +226,4 @@ namespace FreeSql.Internal
public Attribute[] Attributes { get; set; }
}
}
-#endif
}
\ 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..dec44591
--- /dev/null
+++ b/Extensions/FreeSql.Extensions.DynamicEntity/DynamicCompileHelper.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Security.Cryptography;
+using System.Text;
+
+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 cacheKeyStr = string.Join("-", porpertys.Keys.OrderBy(s => s));
+ var dicKey = Md5Encryption(cacheKeyStr);
+ var cacheKey = $"{type.GetHashCode()}-{dicKey}";
+ 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 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;
+ }
+ }
+}
\ 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..321f8a2d
--- /dev/null
+++ b/Extensions/FreeSql.Extensions.DynamicEntity/FreeSql.Extensions.DynamicEntity.csproj
@@ -0,0 +1,42 @@
+
+
+
+ netstandard2.1;net451;net45;
+ True
+ true
+ FreeSql;ncc;YeXiangQin;Daily
+ FreeSql 扩展包,可实现动态构建实体类,动态创建表.
+ https://github.com/2881099/FreeSql
+ https://github.com/2881099/FreeSql
+ git
+ MIT
+ FreeSql;ORM
+ $(AssemblyName)
+ logo.png
+ $(AssemblyName)
+ true
+ true
+ true
+ key.snk
+ false
+ 3.2.694 $(AssemblyName)
+ logo.png
+ $(AssemblyName)
+ true
+ true
+ true
+ key.snk
+ false
+ FreeSql.Extensions.DynamicEntity.xml
+
+
+
+
+
+
+
+ Always
+
+
+
+
\ No newline at end of file
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..a2ae1e55
--- /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
+
+
+
+
+
+ 委托缓存
+
+
+
+
+ 设置动态对象的属性值
+
+
+
+
+
+
+
diff --git a/Extensions/FreeSql.Extensions.DynamicEntity/key.snk b/Extensions/FreeSql.Extensions.DynamicEntity/key.snk
new file mode 100644
index 00000000..e580bc8d
Binary files /dev/null and b/Extensions/FreeSql.Extensions.DynamicEntity/key.snk differ
diff --git a/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs b/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs
new file mode 100644
index 00000000..77579f8c
--- /dev/null
+++ b/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FreeSql.DataAnnotations;
+using FreeSql.Extensions.DynamicEntity;
+using Xunit;
+
+namespace FreeSql.Tests.DynamicEntity
+{
+ public class DynamicEntityTest
+ {
+ private static IFreeSql fsql = new FreeSqlBuilder().UseConnectionString(DataType.PostgreSQL,
+ "Host=192.168.0.36;Port=5432;Username=postgres;Password=123; Database=test;ArrayNullabilityMode=Always;Pooling=true;Minimum Pool Size=1")
+ .UseMonitorCommand(d => Console.WriteLine(d.CommandText)).Build();
+
+ [Fact]
+ public void NormalTest()
+ {
+ Type type = DynamicCompileHelper.DynamicBuilder()
+ .Class("NormalUsers")
+ .Property("Id", typeof(string))
+ .Property("Name", typeof(string))
+ .Property("Address", typeof(string))
+ .Build();
+ var dict = new Dictionary
+ {
+ ["Name"] = "张三",
+ ["Id"] = Guid.NewGuid().ToString(),
+ ["Address"] = "北京市"
+ };
+ var instance = DynamicCompileHelper.CreateObjectByType(type, dict);
+ //根据Type生成表
+ fsql.CodeFirst.SyncStructure(type);
+ fsql.Insert