diff --git a/Examples/base_entity/Program.cs b/Examples/base_entity/Program.cs index 7837fc37..108d57ec 100644 --- a/Examples/base_entity/Program.cs +++ b/Examples/base_entity/Program.cs @@ -600,6 +600,12 @@ namespace base_entity BaseEntity.Initialization(fsql, () => _asyncUow.Value); #endregion + FreeSql.Internal.Utils.TypeHandlers.TryAdd(typeof(TestIdAndIdentity), new String_TestIdAndIdentity()); + fsql.Insert(new TypeHandler01 { id = Guid.NewGuid(), json = new TestIdAndIdentity { Id = 101, IdentityId = 10101 } }).ExecuteAffrows(); + fsql.Insert(new TypeHandler01 { id = Guid.NewGuid(), json = new TestIdAndIdentity { Id = 102, IdentityId = 10202 } }).ExecuteAffrows(); + + var th01s = fsql.Select().ToList(); + var testr1 = fsql.Ado.ExecuteConnectTest(); var dict = new List>(); @@ -2578,11 +2584,29 @@ var sql11111 = fsql.Select() public ProducerConfig PConfig { get; set; } } - class TestIdAndIdentity +class TestIdAndIdentity +{ + [Column(IsPrimary = true)] + public int Id { get; set; } + [Column(IsIdentity = true)] + public int IdentityId { get; set; } +} + +class TypeHandler01 +{ + public Guid id { get; set; } + [Column(MapType = typeof(string), StringLength = -1)] + public TestIdAndIdentity json { get; set; } +} +class String_TestIdAndIdentity : TypeHandler +{ + public override object Serialize(TestIdAndIdentity value) { - [Column(IsPrimary = true)] - public int Id { get; set; } - [Column(IsIdentity = true)] - public int IdentityId { get; set; } + return JsonConvert.SerializeObject(value); + } + public override TestIdAndIdentity Deserialize(object value) + { + return JsonConvert.DeserializeObject((string)value); } } +} diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 594fbad3..537315e2 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -733,6 +733,15 @@ + + + 根据Assembly扫描所有继承IEntityTypeConfiguration<T>的配置类 + + + + + + 创建普通数据上下文档对象 diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 368317df..9e33336b 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1084,82 +1084,6 @@ - - - 动态创建实体类型 - - - - - 配置Class - - 类名 - 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] - - - - - 配置属性 - - 属性名称 - 属性类型 - 属性标记的特性-支持多个 - - - - - 配置属性 - - 属性名称 - 属性类型 - 该属性是否重写父类属性 - 属性标记的特性-支持多个 - - - - - 配置属性 - - 属性名称 - 属性类型 - 该属性是否重写父类属性 - 属性默认值 - 属性标记的特性-支持多个 - - - - - 配置父类 - - 父类类型 - - - - - Override属性 - - - - - - Emit动态创建出Class - Type - - - - - - 首字母小写 - - - - - - - 首字母大写 - - - - 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 null @@ -5824,28 +5748,6 @@ 请使用 fsql.InsertDict(dict) 方法插入字典数据 - - - 动态构建Class Type - - - - - - 根据字典,创建 table 对应的实体对象 - - - - - - - - 根据实体对象,创建 table 对应的字典 - - - - - C#: that >= between && that <= and diff --git a/FreeSql/FreeSqlBuilder.cs b/FreeSql/FreeSqlBuilder.cs index 901fc8f1..2ba9a855 100644 --- a/FreeSql/FreeSqlBuilder.cs +++ b/FreeSql/FreeSqlBuilder.cs @@ -7,6 +7,10 @@ using System.Reflection; using FreeSql.DataAnnotations; using FreeSql.Internal; using FreeSql.Internal.CommonProvider; +using System.Linq.Expressions; +using System.Runtime; +using FreeSql.Internal.Model.Interface; +using System.Threading; namespace FreeSql { @@ -285,7 +289,7 @@ namespace FreeSql type = Type.GetType("FreeSql.Odbc.Default.OdbcProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.Default.OdbcProvider<>"); break; - + case DataType.OdbcDameng: type = Type.GetType("FreeSql.Odbc.Dameng.OdbcDamengProvider`1,FreeSql.Provider.Odbc")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Odbc.dll", "FreeSql.Odbc.Dameng.OdbcDamengProvider<>"); @@ -366,7 +370,7 @@ namespace FreeSql type = Type.GetType("FreeSql.Custom.PostgreSQL.CustomPostgreSQLProvider`1,FreeSql.Provider.Custom")?.MakeGenericType(typeof(TMark)); if (type == null) throwNotFind("FreeSql.Provider.Custom.dll", "FreeSql.Custom.PostgreSQL.CustomPostgreSQLProvider<>"); break; - + default: throw new Exception(CoreStrings.NotSpecified_UseConnectionString_UseConnectionFactory); } } @@ -374,11 +378,11 @@ namespace FreeSql if (_isAdoConnectionPool != null) isAdoPool = _isAdoConnectionPool.Value; else if (_dataType == DataType.Sqlite) isAdoPool = true; //sqlite 默认使用 Ado Pool else isAdoPool = false; - ret = Activator.CreateInstance(type, new object[] + ret = Activator.CreateInstance(type, new object[] { - isAdoPool ? $"AdoConnectionPool,{_masterConnectionString}" : _masterConnectionString, - _slaveConnectionString, - _connectionFactory + isAdoPool ? $"AdoConnectionPool,{_masterConnectionString}" : _masterConnectionString, + _slaveConnectionString, + _connectionFactory }) as IFreeSql; if (ret != null) { @@ -445,7 +449,8 @@ namespace FreeSql } catch { } - var dyattr = attrs?.Where(a => { + var dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.Name == "MaxLengthAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -457,7 +462,8 @@ namespace FreeSql } } - dyattr = attrs?.Where(a => { + dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.RequiredAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -465,7 +471,8 @@ namespace FreeSql e.ModifyResult.IsNullable = false; } - dyattr = attrs?.Where(a => { + dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -473,7 +480,8 @@ namespace FreeSql e.ModifyResult.IsIgnore = true; } - dyattr = attrs?.Where(a => { + dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.ColumnAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -490,7 +498,8 @@ namespace FreeSql e.ModifyResult.DbType = typeName; } - dyattr = attrs?.Where(a => { + dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.KeyAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -498,7 +507,8 @@ namespace FreeSql e.ModifyResult.IsPrimary = true; } - dyattr = attrs?.Where(a => { + dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.StringLengthAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -512,12 +522,13 @@ namespace FreeSql } //https://github.com/dotnetcore/FreeSql/issues/378 - dyattr = attrs?.Where(a => { + dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute"; }).FirstOrDefault(); if (dyattr != null) { - switch(string.Concat(dyattr.GetType().GetProperty("DatabaseGeneratedOption")?.GetValue(dyattr, null))) + switch (string.Concat(dyattr.GetType().GetProperty("DatabaseGeneratedOption")?.GetValue(dyattr, null))) { case "Identity": case "1": @@ -540,7 +551,8 @@ namespace FreeSql } catch { } - var dyattr = attrs?.Where(a => { + var dyattr = attrs?.Where(a => + { return ((a as Attribute)?.TypeId as Type)?.FullName == "System.ComponentModel.DataAnnotations.Schema.TableAttribute"; }).FirstOrDefault(); if (dyattr != null) @@ -565,7 +577,59 @@ namespace FreeSql (ret.Select() as Select0Provider)._commonUtils.IsQuoteSqlName = _isQuoteSqlName; } + if (Interlocked.CompareExchange(ref _isTypeHandlered, 1, 0) == 0) + { + FreeSql.Internal.Utils.GetDataReaderValueBlockExpressionSwitchTypeFullName.Add((LabelTarget returnTarget, Expression valueExp, Type type2) => + { + if (FreeSql.Internal.Utils.TypeHandlers.TryGetValue(type2, out var typeHandler)) return Expression.IfThenElse( + Expression.TypeIs(valueExp, type2), + Expression.Return(returnTarget, valueExp), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call( + Expression.Constant(typeHandler, typeof(ITypeHandler)), + typeof(ITypeHandler).GetMethod(nameof(typeHandler.Deserialize)), + Expression.Convert(valueExp, typeof(object))), type2)) + ); + return null; + }); + } + + ret.Aop.ConfigEntityProperty += (s, e) => + { + foreach (var typeHandler in FreeSql.Internal.Utils.TypeHandlers.Values) + { + if (e.Property.PropertyType == typeHandler.Type) + { + if (_dicTypeHandlerTypes.ContainsKey(e.Property.PropertyType) == false && + FreeSql.Internal.Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(e.Property.PropertyType)) + return; //基础类型无效 + + if (_dicTypeHandlerTypes.TryAdd(e.Property.PropertyType, true)) + { + lock (_concurrentObj) + { + FreeSql.Internal.Utils.dicExecuteArrayRowReadClassOrTuple[e.Property.PropertyType] = true; + FreeSql.Internal.Utils.GetDataReaderValueBlockExpressionObjectToStringIfThenElse.Add((LabelTarget returnTarget, Expression valueExp, Expression elseExp, Type type2) => + { + return Expression.IfThenElse( + Expression.TypeIs(valueExp, e.Property.PropertyType), + Expression.Return(returnTarget, Expression.Call( + Expression.Constant(typeHandler, typeof(ITypeHandler)), + typeof(ITypeHandler).GetMethod(nameof(typeHandler.Serialize)), + Expression.Convert(valueExp, typeof(object)) + ), typeof(object)), + elseExp); + }); + } + } + break; + } + } + }; + return ret; } + static int _isTypeHandlered = 0; + ConcurrentDictionary _dicTypeHandlerTypes = new ConcurrentDictionary(); + object _concurrentObj = new object(); } } diff --git a/FreeSql/Internal/FreeSqlBuilderTypes.cs b/FreeSql/Internal/FreeSqlBuilderTypes.cs index 4910e886..66df5c9f 100644 --- a/FreeSql/Internal/FreeSqlBuilderTypes.cs +++ b/FreeSql/Internal/FreeSqlBuilderTypes.cs @@ -78,5 +78,4 @@ namespace FreeSql.Internal /// ToLower } - } \ No newline at end of file diff --git a/FreeSql/Internal/Model/TypeHandler.cs b/FreeSql/Internal/Model/TypeHandler.cs new file mode 100644 index 00000000..a07280b8 --- /dev/null +++ b/FreeSql/Internal/Model/TypeHandler.cs @@ -0,0 +1,26 @@ +using FreeSql.Internal.Model.Interface; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FreeSql.Internal.Model +{ + namespace Interface + { + public interface ITypeHandler + { + Type Type { get; } + object Deserialize(object value); + object Serialize(object value); + } + } + public abstract class TypeHandler : ITypeHandler + { + public abstract T Deserialize(object value); + public abstract object Serialize(T value); + + public Type Type => typeof(T); + object ITypeHandler.Deserialize(object value) => this.Deserialize(value); + object ITypeHandler.Serialize(object value) => this.Serialize((T)value); + } +} diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index fa4fa306..38513aab 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -1,5 +1,6 @@ using FreeSql.DataAnnotations; using FreeSql.Internal.Model; +using FreeSql.Internal.Model.Interface; using System; using System.Collections; using System.Collections.Concurrent; @@ -2170,6 +2171,8 @@ namespace FreeSql.Internal static MethodInfo MethodGuidToBytes = typeof(Utils).GetMethod("GuidToBytes", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(Guid) }, null); static MethodInfo MethodBytesToGuid = typeof(Utils).GetMethod("BytesToGuid", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(byte[]) }, null); + + public static ConcurrentDictionary TypeHandlers { get; } = new ConcurrentDictionary(); public static ConcurrentBag> GetDataReaderValueBlockExpressionSwitchTypeFullName = new ConcurrentBag>(); public static ConcurrentBag> GetDataReaderValueBlockExpressionObjectToStringIfThenElse = new ConcurrentBag>(); public static ConcurrentBag> GetDataReaderValueBlockExpressionObjectToBytesIfThenElse = new ConcurrentBag>(); @@ -2562,7 +2565,9 @@ namespace FreeSql.Internal { //if (value == null || value == DBNull.Value) return Activator.CreateInstance(type); if (type == null) return value; - var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary>()).GetOrAdd(value?.GetType() ?? type, valueType => + var valueType = value?.GetType() ?? type; + if (TypeHandlers.TryGetValue(valueType, out var typeHandler)) return typeHandler.Serialize(value); + var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary>()).GetOrAdd(valueType, valueType2 => { var parmExp = Expression.Parameter(typeof(object), "value"); var exp = GetDataReaderValueBlockExpression(type, parmExp);