Skip to content

Commit a0f4c90

Browse files
authored
Merge pull request #1497 from d4ilys/master
增加动态操作表结构相关的API
2 parents 4e949a2 + f8866f2 commit a0f4c90

File tree

3 files changed

+336
-0
lines changed

3 files changed

+336
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using System.Text;
5+
using FreeSql.DataAnnotations;
6+
using FreeSql.Internal;
7+
8+
namespace FreeSql.Extensions
9+
{
10+
#if net40 || NETSTANDARD2_0
11+
#else
12+
public static class CodeFirstExtensions
13+
{
14+
/// <summary>
15+
/// 动态构建Class Type
16+
/// </summary>
17+
/// <returns></returns>
18+
public static DynamicCompileBuilder DynamicEntity(this ICodeFirst codeFirst, string className,
19+
TableAttribute tableAttribute)
20+
{
21+
return new DynamicCompileBuilder().SetClass(className, tableAttribute);
22+
}
23+
24+
}
25+
#endif
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using FreeSql.Internal;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Reflection;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace DynamicBuilder
10+
{
11+
#if net40 || NETSTANDARD2_0
12+
#else
13+
public static class TypeExtensions
14+
{
15+
/// <summary>
16+
/// 根据动态构建的Class Type生成实例并进行属性赋值
17+
/// </summary>
18+
/// <param name="type"></param>
19+
/// <param name="porpertys"></param>
20+
/// <returns></returns>
21+
public static object CreateDynamicEntityInstance(this Type type, IFreeSql fsql,
22+
Dictionary<string, object> porpertys)
23+
{
24+
return DynamicCompileBuilder.CreateObjectByTypeByCodeFirst(fsql, type, porpertys);
25+
}
26+
27+
/// <summary>
28+
/// 设置对象属性值
29+
/// </summary>
30+
/// <param name="fsql"></param>
31+
/// <returns></returns>
32+
public static void SetPropertyValue(this Type type, IFreeSql fsql, ref object obj, string propertyName,
33+
object propertyValue)
34+
{
35+
var table = fsql.CodeFirst.GetTableByEntity(obj.GetType());
36+
table.ColumnsByCs[propertyName].SetValue(obj, propertyValue);
37+
}
38+
}
39+
#endif
40+
}
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
using FreeSql.DataAnnotations;
2+
using System;
3+
using System.Collections;
4+
using System.Collections.Concurrent;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Linq.Expressions;
8+
using System.Reflection;
9+
using System.Reflection.Emit;
10+
using System.Security.Cryptography;
11+
using FreeSql.Internal.Model;
12+
using System.Text;
13+
14+
namespace FreeSql.Internal
15+
{
16+
#if net40 || NETSTANDARD2_0
17+
#else
18+
public class DynamicCompileBuilder
19+
{
20+
private string _className = string.Empty;
21+
private TableAttribute _tableAttribute = null;
22+
private List<DynamicPropertyInfo> _properties = new List<DynamicPropertyInfo>();
23+
24+
/// <summary>
25+
/// 配置Class
26+
/// </summary>
27+
/// <param name="className">类名</param>
28+
/// <param name="tableAttribute">类标记的特性[Table(Name = "xxx")]</param>
29+
/// <returns></returns>
30+
public DynamicCompileBuilder SetClass(string className, TableAttribute tableAttribute)
31+
{
32+
_className = className;
33+
_tableAttribute = tableAttribute;
34+
return this;
35+
}
36+
37+
/// <summary>
38+
/// 配置属性
39+
/// </summary>
40+
/// <param name="propertyName">属性名称</param>
41+
/// <param name="propertyType">属性类型</param>
42+
/// <param name="attributes">属性标记的特性-支持多个</param>
43+
/// <returns></returns>
44+
public DynamicCompileBuilder Property(string propertyName, Type propertyType, params Attribute[] attributes)
45+
{
46+
_properties.Add(new DynamicPropertyInfo()
47+
{
48+
PropertyName = propertyName,
49+
PropertyType = propertyType,
50+
Attributes = attributes
51+
});
52+
return this;
53+
}
54+
55+
private void SetTableAttribute(ref TypeBuilder typeBuilder)
56+
{
57+
var classCtorInfo = typeof(TableAttribute).GetConstructor(new Type[] { });
58+
var propertyInfos = typeof(TableAttribute).GetProperties().Where(p => p.CanWrite == true).ToArray();
59+
if (_tableAttribute == null)
60+
{
61+
return;
62+
}
63+
64+
var propertyValues = new ArrayList();
65+
foreach (var propertyInfo in _tableAttribute.GetType().GetProperties().Where(p => p.CanWrite == true))
66+
{
67+
propertyValues.Add(propertyInfo.GetValue(_tableAttribute));
68+
}
69+
70+
var customAttributeBuilder =
71+
new CustomAttributeBuilder(classCtorInfo, new object[0], propertyInfos, propertyValues.ToArray());
72+
typeBuilder.SetCustomAttribute(customAttributeBuilder);
73+
}
74+
75+
private void SetPropertys(ref TypeBuilder typeBuilder)
76+
{
77+
foreach (var pinfo in _properties)
78+
{
79+
var propertyName = pinfo.PropertyName;
80+
var propertyType = pinfo?.PropertyType ?? typeof(object);
81+
//设置字段
82+
var field = typeBuilder.DefineField($"_{FirstCharToLower(propertyName)}", propertyType,
83+
FieldAttributes.Private);
84+
var firstCharToUpper = FirstCharToUpper(propertyName);
85+
//设置属性方法
86+
var methodGet = typeBuilder.DefineMethod($"Get{firstCharToUpper}", MethodAttributes.Public,
87+
propertyType, null);
88+
var methodSet = typeBuilder.DefineMethod($"Set{firstCharToUpper}", MethodAttributes.Public, null,
89+
new Type[] { propertyType });
90+
91+
var ilOfGet = methodGet.GetILGenerator();
92+
ilOfGet.Emit(OpCodes.Ldarg_0);
93+
ilOfGet.Emit(OpCodes.Ldfld, field);
94+
ilOfGet.Emit(OpCodes.Ret);
95+
96+
var ilOfSet = methodSet.GetILGenerator();
97+
ilOfSet.Emit(OpCodes.Ldarg_0);
98+
ilOfSet.Emit(OpCodes.Ldarg_1);
99+
ilOfSet.Emit(OpCodes.Stfld, field);
100+
ilOfSet.Emit(OpCodes.Ret);
101+
102+
//设置属性
103+
var propertyBuilder =
104+
typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null);
105+
propertyBuilder.SetGetMethod(methodGet);
106+
propertyBuilder.SetSetMethod(methodSet);
107+
108+
foreach (var pinfoAttribute in pinfo.Attributes)
109+
{
110+
//设置特性
111+
SetPropertyAttribute(ref propertyBuilder, pinfoAttribute);
112+
}
113+
}
114+
}
115+
116+
private void SetPropertyAttribute<T>(ref PropertyBuilder propertyBuilder, T tAttribute)
117+
{
118+
if (tAttribute == null)
119+
return;
120+
121+
var propertyValues = new ArrayList();
122+
foreach (var propertyInfo in tAttribute.GetType().GetProperties().Where(p => p.CanWrite == true))
123+
{
124+
propertyValues.Add(propertyInfo.GetValue(tAttribute));
125+
}
126+
127+
var propertyInfos = tAttribute.GetType().GetProperties().Where(p => p.CanWrite == true).ToArray();
128+
var constructor = tAttribute.GetType().GetConstructor(new Type[] { });
129+
var customAttributeBuilder =
130+
new CustomAttributeBuilder(constructor, new object[0], propertyInfos, propertyValues.ToArray());
131+
propertyBuilder.SetCustomAttribute(customAttributeBuilder);
132+
}
133+
134+
/// <summary>
135+
/// Emit动态创建出Class - Type
136+
/// </summary>
137+
/// <returns></returns>
138+
public Type Build()
139+
{
140+
//初始化AssemblyName的一个实例
141+
var assemblyName = new AssemblyName("FreeSql.DynamicCompileBuilder");
142+
//设置程序集的名称
143+
var defineDynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
144+
//动态在程序集内创建一个模块
145+
var defineDynamicModule =
146+
defineDynamicAssembly.DefineDynamicModule("FreeSql.DynamicCompileBuilder.Dynamics");
147+
//动态的在模块内创建一个类
148+
var typeBuilder = defineDynamicModule.DefineType(_className, TypeAttributes.Public | TypeAttributes.Class);
149+
150+
//设置TableAttribute
151+
SetTableAttribute(ref typeBuilder);
152+
153+
//设置属性
154+
SetPropertys(ref typeBuilder);
155+
156+
//创建类的Type对象
157+
return typeBuilder.CreateType();
158+
}
159+
160+
//委托缓存
161+
private static ConcurrentDictionary<string, Delegate>
162+
_delegateCache = new ConcurrentDictionary<string, Delegate>();
163+
164+
//设置动态对象的属性值 使用FreeSql自带功能
165+
public static object CreateObjectByTypeByCodeFirst(IFreeSql fsql, Type type,
166+
Dictionary<string, object> porpertys)
167+
{
168+
if (type == null)
169+
return null;
170+
object istance = Activator.CreateInstance(type);
171+
if (istance == null)
172+
return null;
173+
var table = fsql.CodeFirst.GetTableByEntity(type);
174+
foreach (var kv in porpertys)
175+
{
176+
table.ColumnsByCs[kv.Key].SetValue(istance, kv.Value);
177+
}
178+
179+
return istance;
180+
}
181+
182+
////设置动态对象的属性值,使用表达式目录树
183+
//public static object CreateObjectByType(Type type, Dictionary<string, object> porpertys)
184+
//{
185+
// if (type == null)
186+
// return null;
187+
// object istance = Activator.CreateInstance(type);
188+
// if (istance == null)
189+
// return null;
190+
// //根据字典中的key确定缓存
191+
// var cacheKeyStr = string.Join("-", porpertys.Keys.OrderBy(s => s));
192+
// var cacheKey = Md5Encryption(cacheKeyStr);
193+
// var dynamicDelegate = _delegateCache.GetOrAdd(cacheKey, key =>
194+
// {
195+
// //表达式目录树构建委托
196+
// var typeParam = Expression.Parameter(type);
197+
// var dicParamType = typeof(Dictionary<string, object>);
198+
// var dicParam = Expression.Parameter(dicParamType);
199+
// var exps = new List<Expression>();
200+
// var tempRef = Expression.Variable(typeof(object));
201+
// foreach (var pinfo in porpertys)
202+
// {
203+
// var propertyInfo = type.GetProperty(pinfo.Key);
204+
// if (propertyInfo == null)
205+
// continue;
206+
// var propertyName = Expression.Constant(pinfo.Key, typeof(string));
207+
// exps.Add(Expression.Call(dicParam, dicParamType.GetMethod("TryGetValue"), propertyName, tempRef));
208+
// exps.Add(Expression.Assign(Expression.MakeMemberAccess(typeParam, propertyInfo),
209+
// Expression.Convert(tempRef, propertyInfo.PropertyType)));
210+
// exps.Add(Expression.Assign(tempRef, Expression.Default(typeof(object))));
211+
// }
212+
213+
// var returnTarget = Expression.Label(type);
214+
// exps.Add(Expression.Return(returnTarget, typeParam));
215+
// exps.Add(Expression.Label(returnTarget, Expression.Default(type)));
216+
// var block = Expression.Block(new[] { tempRef }, exps);
217+
// var @delegate = Expression.Lambda(block, typeParam, dicParam).Compile();
218+
// return @delegate;
219+
// });
220+
// var dynamicInvoke = dynamicDelegate.DynamicInvoke(istance, porpertys);
221+
// return dynamicInvoke;
222+
//}
223+
224+
/// <summary>
225+
/// 首字母小写
226+
/// </summary>
227+
/// <param name="input"></param>
228+
/// <returns></returns>
229+
private string FirstCharToLower(string input)
230+
{
231+
if (string.IsNullOrEmpty(input))
232+
return input;
233+
string str = input.First().ToString().ToLower() + input.Substring(1);
234+
return str;
235+
}
236+
237+
/// <summary>
238+
/// 首字母大写
239+
/// </summary>
240+
/// <param name="input"></param>
241+
/// <returns></returns>
242+
private string FirstCharToUpper(string input)
243+
{
244+
if (string.IsNullOrEmpty(input))
245+
return input;
246+
string str = input.First().ToString().ToUpper() + input.Substring(1);
247+
return str;
248+
}
249+
250+
private static string Md5Encryption(string inputStr)
251+
{
252+
var result = string.Empty;
253+
//32位大写
254+
using (var md5 = MD5.Create())
255+
{
256+
var resultBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(inputStr));
257+
result = BitConverter.ToString(resultBytes);
258+
}
259+
260+
return result;
261+
}
262+
}
263+
#endif
264+
internal class DynamicPropertyInfo
265+
{
266+
public string PropertyName { get; set; } = string.Empty;
267+
public Type PropertyType { get; set; }
268+
public Attribute[] Attributes { get; set; }
269+
}
270+
}

0 commit comments

Comments
 (0)