From 4732ccde87e725dc14efabb517db1a85f8022cdf Mon Sep 17 00:00:00 2001 From: d4ilys <963922242@qq.com> Date: Mon, 15 May 2023 16:34:55 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=9F=BA=E7=B1=BB=E5=B1=9E=E6=80=A7abstrac?= =?UTF-8?q?t/virtual=E6=94=AF=E6=8C=81override=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DynamicEntity/DynamicEntityTest.cs | 55 ++++++---- FreeSql/Extensions/DynamicEntityExtensions.cs | 100 +++++++++++++++--- FreeSql/FreeSql.xml | 98 +++++++++++++++++ 3 files changed, 219 insertions(+), 34 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs b/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs index 3c988ea8..ec135fcb 100644 --- a/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/DynamicEntity/DynamicEntityTest.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using FreeSql.DataAnnotations; using FreeSql.Extensions.DynamicEntity; +using Newtonsoft.Json; using Xunit; namespace FreeSql.Tests.DynamicEntity @@ -118,7 +119,7 @@ namespace FreeSql.Tests.DynamicEntity var table = fsql.CodeFirst.DynamicEntity("Role_AbstractOverride", new TableAttribute() { Name = "T_Role_AbstractOverride" }, new IndexAttribute("Name_Index2", "Name", false)) - .Extend(typeof(BaseModelOverride)) + .Extend(typeof(BaseModelAbstract)) .Property("Id", typeof(int), new ColumnAttribute() { IsPrimary = true, IsIdentity = true, Position = 1 }) .Property("Name", typeof(string), @@ -145,7 +146,7 @@ namespace FreeSql.Tests.DynamicEntity var table = fsql.CodeFirst.DynamicEntity("Role_AbstractAndVirtualOverride", new TableAttribute() { Name = "Role_AbstractAndVirtualOverride" }, new IndexAttribute("Name_Index2", "Name", false)) - .Extend(typeof(BaseModelOverride)) + .Extend(typeof(BaseModelAbstractAndVirtual)) .Property("Id", typeof(int), new ColumnAttribute() { IsPrimary = true, IsIdentity = true, Position = 1 }) .Property("Name", typeof(string), @@ -167,6 +168,32 @@ namespace FreeSql.Tests.DynamicEntity fsql.Insert().AsType(table.Type).AppendData(instance).ExecuteAffrows(); var objects = fsql.Select().AsType(table.Type).ToList(); } + + [Fact] + public void DefaultValueTest() + { + var table = fsql.CodeFirst.DynamicEntity("NormalUsers") + .Property("Id", typeof(string)) + .Property("Age", typeof(int), false, 12) + .Property("Longs", typeof(long), false, 16666) + .Property("Dates", typeof(DateTime), false, "2023-05-15") + .Property("Name", typeof(char), false, '我') + .Property("Address", typeof(bool), false, false) //设置默认值 + .Property("Money", typeof(double), false, 265421.02) //设置默认值 + .Property("MoneyFloat", typeof(float), false, 26543.02) //设置默认值 + .Property("MoneyDecimal", typeof(decimal), true, 2663.12560) //设置默认值 + .Build(); + + var dict = new Dictionary + { + ["Id"] = Guid.NewGuid().ToString() + }; + var instance = table.CreateInstance(dict); + //根据Type生成表 + fsql.CodeFirst.SyncStructure(table.Type); + fsql.Insert().AsType(table.Type).AppendData(instance).ExecuteAffrows(); + var objects = fsql.Select().AsType(table.Type).ToList(); + } } public class BaseModel @@ -199,28 +226,14 @@ namespace FreeSql.Tests.DynamicEntity public abstract class BaseModelAbstractAndVirtual { - [Column(Position = 99)] - public DateTime UpdateTime - { - get; set; - } + [Column(Position = 99)] public DateTime UpdateTime { get; set; } [Column(Position = 100, StringLength = 20)] - public string UpdatePerson - { - get; set; - } + public string UpdatePerson { get; set; } - public abstract string Operators - { - get; set; - } + public abstract string Operators { get; set; } - public virtual string Operators2 - { - get; set; - } + public virtual string Operators2 { get; set; } } -} - +} \ No newline at end of file diff --git a/FreeSql/Extensions/DynamicEntityExtensions.cs b/FreeSql/Extensions/DynamicEntityExtensions.cs index 0ae32e0e..45873720 100644 --- a/FreeSql/Extensions/DynamicEntityExtensions.cs +++ b/FreeSql/Extensions/DynamicEntityExtensions.cs @@ -11,11 +11,15 @@ using FreeSql.Internal.Model; using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; +using System.Net.NetworkInformation; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Serialization; using System.Security.Cryptography; using System.Text; +using System.Xml.Serialization; public static class FreeSqlGlobalDynamicEntityExtensions { @@ -39,6 +43,12 @@ public static class FreeSqlGlobalDynamicEntityExtensions { if (table == null || dict == null) return null; var instance = table.Type.CreateInstanceGetDefaultValue(); + //加载默认值 + var defaultValueInit = table.Type.GetMethod("DefaultValueInit"); + if (defaultValueInit != null) + { + defaultValueInit.Invoke(instance, new object[0]); + } foreach (var key in table.ColumnsByCs.Keys) { if (dict.ContainsKey(key) == false) continue; @@ -148,7 +158,7 @@ namespace FreeSql.Extensions.DynamicEntity { PropertyName = propertyName, PropertyType = propertyType, - DefaultValue = null, + DefaultValue = defaultValue, IsOverride = isOverride, Attributes = attributes }); @@ -176,7 +186,7 @@ namespace FreeSql.Extensions.DynamicEntity if (tableAttribute == null) continue; - var classCtorInfo = tableAttribute.GetType().GetConstructor(new Type[] { }); + var classCtorInfo = tableAttribute.GetType().GetConstructor(Type.EmptyTypes); var propertyInfos = tableAttribute.GetType().GetProperties().Where(p => p.CanWrite == true).ToArray(); @@ -202,6 +212,7 @@ namespace FreeSql.Extensions.DynamicEntity private void SetPropertys(ref TypeBuilder typeBuilder) { + var defaultValues = new Dictionary(); foreach (var pinfo in _properties) { if (pinfo == null) @@ -210,11 +221,12 @@ namespace FreeSql.Extensions.DynamicEntity var propertyType = pinfo.PropertyType; //设置字段 var field = typeBuilder.DefineField($"_{FirstCharToLower(propertyName)}", propertyType, - FieldAttributes.Private); + FieldAttributes.Private | FieldAttributes.HasDefault); var firstCharToUpper = FirstCharToUpper(propertyName); MethodAttributes maAttributes = MethodAttributes.Public; + //是否重写 if (pinfo.IsOverride) { maAttributes = MethodAttributes.Public | MethodAttributes.Virtual; @@ -251,33 +263,95 @@ namespace FreeSql.Extensions.DynamicEntity propertyBuilder.SetGetMethod(methodGet); propertyBuilder.SetSetMethod(methodSet); - //设置默认值 - if (pinfo.DefaultValue != null) - { - propertyBuilder.SetConstant(pinfo.DefaultValue); - } - foreach (var pinfoAttribute in pinfo.Attributes) { //设置特性 SetPropertyAttribute(ref propertyBuilder, pinfoAttribute); } + + if (pinfo.DefaultValue != null) + { + defaultValues.Add(field, pinfo.DefaultValue); + } + } + + //动态构建方法,设置默认值 + var methodDefaultValue = typeBuilder.DefineMethod($"DefaultValueInit", MethodAttributes.Public, null, null); + var methodDefaultValueLlGenerator = methodDefaultValue.GetILGenerator(); + foreach (var kv in defaultValues) + { + methodDefaultValueLlGenerator.Emit(OpCodes.Ldarg_0); + OpCodesAdapter(ref methodDefaultValueLlGenerator, kv.Key, kv.Value); + methodDefaultValueLlGenerator.Emit(OpCodes.Stfld, kv.Key); + } + + methodDefaultValueLlGenerator.Emit(OpCodes.Ret); + } + + //IL命令类型适配 + private void OpCodesAdapter(ref ILGenerator generator, FieldInfo info, object value) + { + var fieldTypeName = info.FieldType.Name; + switch (fieldTypeName) + { + case "Int32": + generator.Emit(OpCodes.Ldc_I4, Convert.ToInt32(value)); + break; + case "Boolean": + generator.Emit(OpCodes.Ldc_I4, Convert.ToInt32(value)); + break; + case "Char": + generator.Emit(OpCodes.Ldc_I4, Convert.ToChar(value)); + break; + case "String": + generator.Emit(OpCodes.Ldstr, Convert.ToString(value)); + break; + case "DateTime": + generator.Emit(OpCodes.Ldstr, Convert.ToString(value)); + generator.Emit(OpCodes.Call, typeof(DateTime).GetMethod("Parse", new[] { typeof(string) })); + break; + case "Int64": + generator.Emit(OpCodes.Ldc_I4, Convert.ToString(value)); + generator.Emit(OpCodes.Conv_I8); + break; + case "Double": + generator.Emit(OpCodes.Ldc_R8, Convert.ToDouble(value)); + break; + case "Single": + generator.Emit(OpCodes.Ldc_R4, Convert.ToSingle(value)); + break; + case "Decimal": + Console.WriteLine(Convert.ToString(value)); + generator.Emit(OpCodes.Ldstr, Convert.ToString(value)); + generator.Emit(OpCodes.Call, typeof(Decimal).GetMethod("Parse", new[] { typeof(string) })); + break; } } 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 constructor = tAttribute.GetType().GetConstructor(Type.EmptyTypes); 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()); + //可能存在有参构造 + //if (constructor == null) + //{ + // var constructorTypes = propertyInfos.Select(p => p.PropertyType).ToList(); + // constructor = tAttribute.GetType().GetConstructor(constructorTypes.ToArray()); + // var customAttributeBuilder = new CustomAttributeBuilder(constructor, constructorTypes.ToArray(), + // propertyInfos, propertyValues.ToArray()); + // propertyBuilder.SetCustomAttribute(customAttributeBuilder); + //} + //else + //{ + var customAttributeBuilder = new CustomAttributeBuilder(constructor, new object[0], propertyInfos, + propertyValues.ToArray()); propertyBuilder.SetCustomAttribute(customAttributeBuilder); + // } } /// diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 5323d7da..8cd7ad2d 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1073,6 +1073,82 @@ + + + 动态创建实体类型 + + + + + 配置Class + + 类名 + 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] + + + + + 配置属性 + + 属性名称 + 属性类型 + 属性标记的特性-支持多个 + + + + + 配置属性 + + 属性名称 + 属性类型 + 该属性是否重写父类属性 + 属性标记的特性-支持多个 + + + + + 配置属性 + + 属性名称 + 属性类型 + 该属性是否重写父类属性 + 属性默认值 + 属性标记的特性-支持多个 + + + + + 配置父类 + + 父类类型 + + + + + Override属性 + + + + + + Emit动态创建出Class - Type + + + + + + 首字母小写 + + + + + + + 首字母大写 + + + + 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 null @@ -5692,6 +5768,28 @@ 请使用 fsql.InsertDict(dict) 方法插入字典数据 + + + 动态构建Class Type + + + + + + 根据字典,创建 table 对应的实体对象 + + + + + + + + 根据实体对象,创建 table 对应的字典 + + + + + C#: that >= between && that <= and