diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs index f08afce6..d5b6835d 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProvider.cs @@ -2,11 +2,13 @@ using SafeObjectPool; using System; using System.Collections.Generic; +using System.Collections.Concurrent; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Linq; using System.Text; +using System.Reflection; namespace FreeSql.Internal.CommonProvider { abstract partial class AdoProvider : IAdo { @@ -62,15 +64,21 @@ namespace FreeSql.Internal.CommonProvider { if (isThrowException) throw e; } + internal static ConcurrentDictionary> dicQueryTypeGetProperties = new ConcurrentDictionary>(); public List Query(string cmdText, object parms = null) => Query(CommandType.Text, cmdText, GetDbParamtersByObject(cmdText, parms)); public List Query(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) { - var names = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var ret = new List(); var type = typeof(T); + int[] indexes = null; + var props = dicQueryTypeGetProperties.GetOrAdd(type, k => type.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase)); ExecuteReader(dr => { - if (names.Any() == false) - for (var a = 0; a < dr.FieldCount; a++) names.Add(dr.GetName(a), a); - ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, names, dr, 0).Value); + if (indexes == null) { + var idxs = new List(); + for (var a = 0; a < dr.FieldCount; a++) + if (props.ContainsKey(dr.GetName(a))) idxs.Add(a); + indexes = idxs.ToArray(); + } + ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, indexes, dr, 0).Value); }, cmdType, cmdText, cmdParms); return ret; } diff --git a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs index b9c0fd3f..2af90805 100644 --- a/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs +++ b/FreeSql/Internal/CommonProvider/AdoProvider/AdoProviderAsync.cs @@ -11,13 +11,18 @@ namespace FreeSql.Internal.CommonProvider { partial class AdoProvider { public Task> QueryAsync(string cmdText, object parms = null) => QueryAsync(CommandType.Text, cmdText, GetDbParamtersByObject(cmdText, parms)); async public Task> QueryAsync(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) { - var names = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var ret = new List(); var type = typeof(T); + int[] indexes = null; + var props = dicQueryTypeGetProperties.GetOrAdd(type, k => type.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase)); await ExecuteReaderAsync(dr => { - if (names.Any() == false) - for (var a = 0; a < dr.FieldCount; a++) names.Add(dr.GetName(a), a); - ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, names, dr, 0).Value); + if (indexes == null) { + var idxs = new List(); + for (var a = 0; a < dr.FieldCount; a++) + if (props.ContainsKey(dr.GetName(a))) idxs.Add(a); + indexes = idxs.ToArray(); + } + ret.Add((T)Utils.ExecuteArrayRowReadClassOrTuple(type, indexes, dr, 0).Value); return Task.CompletedTask; }, cmdType, cmdText, cmdParms); return ret; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 0721bde7..20895c9c 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -271,6 +271,7 @@ namespace FreeSql.Internal.CommonProvider { var dicfield = new Dictionary(); var tb = _tables.First(); var index = 0; + var otherindex = 0; var ps = _tables.First().Table.Properties; foreach (var prop in ps.Values) { if (tb.Table.ColumnsByCs.TryGetValue(prop.Name, out var col)) { //普通字段 @@ -289,6 +290,7 @@ namespace FreeSql.Internal.CommonProvider { var quoteName = _commonUtils.QuoteSqlName(col2.Attribute.Name); field.Append(_commonUtils.QuoteReadColumn(col2.CsType, $"{tb2.Alias}.{quoteName}")); ++index; + ++otherindex; if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index); else dicfield.Add(quoteName, true); } @@ -297,7 +299,8 @@ namespace FreeSql.Internal.CommonProvider { var propGetSetMethod = prop.GetSetMethod(); Expression readExpAssign = null; //加速缓存 if (prop.PropertyType.IsArray) readExpAssign = Expression.New(Utils.RowInfo.Constructor, - Expression.Call(Utils.MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp) }), + Utils.GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(Utils.MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { @@ -305,27 +308,29 @@ namespace FreeSql.Internal.CommonProvider { if (proptypeGeneric.FullName.StartsWith("System.Nullable`1[")) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First(); if (proptypeGeneric.IsEnum || Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(Utils.RowInfo.Constructor, - Expression.Call(Utils.MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp) }), - Expression.Add(dataIndexExp, Expression.Constant(1)) + Utils.GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(Utils.MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp) }), + Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { - readExpAssign = Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Constant(null, typeof(Dictionary)), rowExp, dataIndexExp }); + readExpAssign = Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp }); } } blockExp.AddRange(new Expression[] { - //以下注释部分为【严格读取】,会损失一点性能 - //Expression.IfThen(Expression.Not(Expression.And( - // Expression.NotEqual(namesExp, Expression.Constant(null)), - // Expression.Not(Expression.Call(namesExp, namesExp.Type.GetMethod("TryGetValue"), Expression.Constant(prop.Name), tryidxExp)))), - // Expression.Block( - Expression.Assign(readExp, readExpAssign), - Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), - Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), - Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), - Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) - // ) - //) + Expression.Assign(readExp, readExpAssign), + Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), + Expression.Assign(dataIndexExp, readExpDataIndex)), + Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), + Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), + Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) + }); + } + if (otherindex == 0) { //不读导航属性,优化单表读取性能 + blockExp.Clear(); + blockExp.AddRange(new Expression[] { + Expression.Assign(dataIndexExp, Expression.Constant(0)), + Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(type), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp })), + Expression.Assign(retExp, Expression.Convert(readExpValue, type)) }); } blockExp.AddRange(new Expression[] { diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index b6c5c4b1..f158dc79 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -24,77 +24,78 @@ namespace FreeSql.Internal { static ConcurrentDictionary _cacheGetTableByEntity = new ConcurrentDictionary(); internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) { if (entity.FullName.StartsWith("<>f__AnonymousType")) return null; - if (_cacheGetTableByEntity.TryGetValue($"{common.DbName}-{entity.FullName}", out var trytb)) return trytb; //区分数据库类型缓存 - if (common.CodeFirst.GetDbInfo(entity) != null) return null; + return _cacheGetTableByEntity.GetOrAdd($"{common.DbName}-{entity.FullName}", key => { //区分数据库类型缓存 + if (common.CodeFirst.GetDbInfo(entity) != null) return null; - var tbattr = entity.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute; - trytb = new TableInfo(); - trytb.Type = entity; - trytb.Properties = entity.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase); - trytb.CsName = entity.Name; - trytb.DbName = (tbattr?.Name ?? entity.Name); - trytb.DbOldName = tbattr?.OldName; - if (common.CodeFirst.IsSyncStructureToLower) { - trytb.DbName = trytb.DbName.ToLower(); - trytb.DbOldName = trytb.DbOldName?.ToLower(); - } - trytb.SelectFilter = tbattr?.SelectFilter; - foreach (var p in trytb.Properties.Values) { - var tp = common.CodeFirst.GetDbInfo(p.PropertyType); - //if (tp == null) continue; - var colattr = p.GetCustomAttributes(typeof(ColumnAttribute), false).LastOrDefault() as ColumnAttribute; - if (tp == null && colattr == null) continue; - if (colattr == null) - colattr = new ColumnAttribute { - Name = p.Name, - DbType = tp.Value.dbtypeFull, - IsIdentity = false, - IsNullable = tp.Value.isnullable ?? true, - IsPrimary = false, + var tbattr = entity.GetCustomAttributes(typeof(TableAttribute), false).LastOrDefault() as TableAttribute; + var trytb = new TableInfo(); + trytb.Type = entity; + trytb.Properties = entity.GetProperties().ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase); + trytb.CsName = entity.Name; + trytb.DbName = (tbattr?.Name ?? entity.Name); + trytb.DbOldName = tbattr?.OldName; + if (common.CodeFirst.IsSyncStructureToLower) { + trytb.DbName = trytb.DbName.ToLower(); + trytb.DbOldName = trytb.DbOldName?.ToLower(); + } + trytb.SelectFilter = tbattr?.SelectFilter; + foreach (var p in trytb.Properties.Values) { + var tp = common.CodeFirst.GetDbInfo(p.PropertyType); + //if (tp == null) continue; + var colattr = p.GetCustomAttributes(typeof(ColumnAttribute), false).LastOrDefault() as ColumnAttribute; + if (tp == null && colattr == null) continue; + if (colattr == null) + colattr = new ColumnAttribute { + Name = p.Name, + DbType = tp.Value.dbtypeFull, + IsIdentity = false, + IsNullable = tp.Value.isnullable ?? true, + IsPrimary = false, + }; + if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp?.dbtypeFull ?? "varchar(255)"; + colattr.DbType = colattr.DbType.ToUpper(); + + if (tp != null && tp.Value.isnullable == null) colattr.IsNullable = tp.Value.dbtypeFull.Contains("NOT NULL") == false; + if (colattr.DbType?.Contains("NOT NULL") == true) colattr.IsNullable = false; + if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = p.Name; + if (common.CodeFirst.IsSyncStructureToLower) colattr.Name = colattr.Name.ToLower(); + + if ((colattr.IsNullable == false || colattr.IsIdentity || colattr.IsPrimary) && colattr.DbType.Contains("NOT NULL") == false) { + colattr.IsNullable = false; + colattr.DbType += " NOT NULL"; + } + if (colattr.IsNullable == true && colattr.DbType.Contains("NOT NULL")) colattr.DbType = colattr.DbType.Replace("NOT NULL", ""); + colattr.DbType = Regex.Replace(colattr.DbType, @"\([^\)]+\)", m => { + var tmpLt = Regex.Replace(m.Groups[0].Value, @"\s", ""); + if (tmpLt.Contains("CHAR")) tmpLt = tmpLt.Replace("CHAR", " CHAR"); + if (tmpLt.Contains("BYTE")) tmpLt = tmpLt.Replace("BYTE", " BYTE"); + return tmpLt; + }); + colattr.DbDefautValue = trytb.Properties[p.Name].GetValue(Activator.CreateInstance(trytb.Type)); + if (colattr.DbDefautValue == null) colattr.DbDefautValue = tp?.defaultValue; + if (colattr.IsNullable == false && colattr.DbDefautValue == null) { + var consturctorType = p.PropertyType.GenericTypeArguments.FirstOrDefault() ?? p.PropertyType; + colattr.DbDefautValue = Activator.CreateInstance(consturctorType); + } + + var col = new ColumnInfo { + Table = trytb, + CsName = p.Name, + CsType = p.PropertyType, + Attribute = colattr }; - if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp?.dbtypeFull ?? "varchar(255)"; - colattr.DbType = colattr.DbType.ToUpper(); - - if (tp != null && tp.Value.isnullable == null) colattr.IsNullable = tp.Value.dbtypeFull.Contains("NOT NULL") == false; - if (colattr.DbType?.Contains("NOT NULL") == true) colattr.IsNullable = false; - if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = p.Name; - if (common.CodeFirst.IsSyncStructureToLower) colattr.Name = colattr.Name.ToLower(); - - if ((colattr.IsNullable == false || colattr.IsIdentity || colattr.IsPrimary) && colattr.DbType.Contains("NOT NULL") == false) { - colattr.IsNullable = false; - colattr.DbType += " NOT NULL"; + trytb.Columns.Add(colattr.Name, col); + trytb.ColumnsByCs.Add(p.Name, col); } - if (colattr.IsNullable == true && colattr.DbType.Contains("NOT NULL")) colattr.DbType = colattr.DbType.Replace("NOT NULL", ""); - colattr.DbType = Regex.Replace(colattr.DbType, @"\([^\)]+\)", m => { - var tmpLt = Regex.Replace(m.Groups[0].Value, @"\s", ""); - if (tmpLt.Contains("CHAR")) tmpLt = tmpLt.Replace("CHAR", " CHAR"); - if (tmpLt.Contains("BYTE")) tmpLt = tmpLt.Replace("BYTE", " BYTE"); - return tmpLt; - }); - colattr.DbDefautValue = trytb.Properties[p.Name].GetValue(Activator.CreateInstance(trytb.Type)); - if (colattr.DbDefautValue == null) colattr.DbDefautValue = tp?.defaultValue; - if (colattr.IsNullable == false && colattr.DbDefautValue == null) { - var consturctorType = p.PropertyType.GenericTypeArguments.FirstOrDefault() ?? p.PropertyType; - colattr.DbDefautValue = Activator.CreateInstance(consturctorType); + trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary).ToArray(); + if (trytb.Primarys.Any() == false) { + trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity).ToArray(); + foreach (var col in trytb.Primarys) + col.Attribute.IsPrimary = true; } - - var col = new ColumnInfo { - Table = trytb, - CsName = p.Name, - CsType = p.PropertyType, - Attribute = colattr - }; - trytb.Columns.Add(colattr.Name, col); - trytb.ColumnsByCs.Add(p.Name, col); - } - trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary).ToArray(); - if (trytb.Primarys.Any() == false) { - trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity).ToArray(); - foreach(var col in trytb.Primarys) - col.Attribute.IsPrimary = true; - } - _cacheGetTableByEntity.TryAdd(entity.FullName, trytb); - return trytb; + _cacheGetTableByEntity.TryAdd(entity.FullName, trytb); + return trytb; + }); } internal static T[] GetDbParamtersByObject(string sql, object obj, string paramPrefix, Func constructorParamter) { @@ -166,7 +167,7 @@ namespace FreeSql.Internal { [typeof(JObject)] = true, [typeof(JArray)] = true, }; - static ConcurrentDictionary, DbDataReader, int, RowInfo>> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary, DbDataReader, int, RowInfo>>(); + internal static ConcurrentDictionary> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary>(); internal class RowInfo { public object Value { get; set; } public int DataIndex { get; set; } @@ -179,29 +180,31 @@ namespace FreeSql.Internal { public static PropertyInfo PropertyDataIndex = typeof(RowInfo).GetProperty("DataIndex"); } internal static MethodInfo MethodDataReaderGetValue = typeof(DbDataReader).GetMethod("GetValue"); - internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type type, Dictionary names, DbDataReader row, int dataIndex = 0) { + internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type type, int[] indexes, DbDataReader row, int dataIndex = 0) { var func = _dicExecuteArrayRowReadClassOrTuple.GetOrAdd(type, s => { var returnTarget = Expression.Label(typeof(RowInfo)); var typeExp = Expression.Parameter(typeof(Type), "type"); - var namesExp = Expression.Parameter(typeof(Dictionary), "names"); + var indexesExp = Expression.Parameter(typeof(int[]), "indexes"); var rowExp = Expression.Parameter(typeof(DbDataReader), "row"); var dataIndexExp = Expression.Parameter(typeof(int), "dataIndex"); - if (type.IsArray) return Expression.Lambda, DbDataReader, int, RowInfo>>( + if (type.IsArray) return Expression.Lambda>( Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), + GetDataReaderValueBlockExpression(type, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) - ), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile(); + ), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); var typeGeneric = type; if (typeGeneric.FullName.StartsWith("System.Nullable`1[")) typeGeneric = type.GenericTypeArguments.First(); if (typeGeneric.IsEnum || dicExecuteArrayRowReadClassOrTuple.ContainsKey(typeGeneric)) - return Expression.Lambda, DbDataReader, int, RowInfo>>( + return Expression.Lambda>( Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), + GetDataReaderValueBlockExpression(type, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) - ), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile(); + ), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组 bool isTuple = type.Name.StartsWith("ValueTuple`"); @@ -216,7 +219,8 @@ namespace FreeSql.Internal { foreach (var field in fields) { Expression read2ExpAssign = null; //加速缓存 if (field.FieldType.IsArray) read2ExpAssign = Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(field.FieldType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), + GetDataReaderValueBlockExpression(field.FieldType, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(field.FieldType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { @@ -224,18 +228,19 @@ namespace FreeSql.Internal { if (fieldtypeGeneric.FullName.StartsWith("System.Nullable`1[")) fieldtypeGeneric = fieldtypeGeneric.GenericTypeArguments.First(); if (fieldtypeGeneric.IsEnum || dicExecuteArrayRowReadClassOrTuple.ContainsKey(fieldtypeGeneric)) read2ExpAssign = Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(field.FieldType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), - Expression.Add(dataIndexExp, Expression.Constant(1)) + GetDataReaderValueBlockExpression(field.FieldType, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(field.FieldType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), + Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { - read2ExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(field.FieldType), namesExp, rowExp, dataIndexExp }); + read2ExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(field.FieldType), indexesExp, rowExp, dataIndexExp }); } } block2Exp.AddRange(new Expression[] { //Expression.TryCatch(Expression.Block( // typeof(void), Expression.Assign(read2Exp, read2ExpAssign), - Expression.IfThen(Expression.GreaterThan(read2ExpDataIndex, dataIndexExp), + Expression.IfThen(Expression.GreaterThan(read2ExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, read2ExpDataIndex)), Expression.IfThenElse(Expression.Equal(read2ExpValue, Expression.Constant(null)), Expression.Assign(Expression.MakeMemberAccess(ret2Exp, field), Expression.Default(field.FieldType)), @@ -255,31 +260,33 @@ namespace FreeSql.Internal { Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, Expression.Convert(ret2Exp, typeof(object)), dataIndexExp)), Expression.Label(returnTarget, Expression.Default(typeof(RowInfo))) }); - return Expression.Lambda, DbDataReader, int, RowInfo>>( - Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile(); + return Expression.Lambda>( + Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); } var rowLenExp = Expression.ArrayLength(rowExp); - return Expression.Lambda, DbDataReader, int, RowInfo>>( + return Expression.Lambda>( Expression.Block( Expression.IfThen( Expression.LessThan(dataIndexExp, rowLenExp), - Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), + Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, + GetDataReaderValueBlockExpression(type, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { typeExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)))) ), Expression.Label(returnTarget, Expression.Default(typeof(RowInfo))) - ), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile(); + ), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); } - if (type == typeof(object) && names != null) { - Func, DbDataReader, int, RowInfo> dynamicFunc = (type2, names2, row2, dataindex2) => { + if (type == typeof(object) && indexes != null) { + Func dynamicFunc = (type2, indexes2, row2, dataindex2) => { dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写 var expandodic = (IDictionary)expando; - foreach (var name in names2) - expandodic.Add(name.Key, row2.GetValue(name.Value)); - return new RowInfo(expando, names2.Count); + var fc = row2.FieldCount; + for (var a = 0; a < fc; a++) + expandodic.Add(row2.GetName(a), row2.GetValue(a)); + return new RowInfo(expando, fc); }; - return dynamicFunc;// Expression.Lambda, DbDataReader, int, RowInfo>>(null); + return dynamicFunc;// Expression.Lambda>(null); } //类注入属性 @@ -290,14 +297,16 @@ namespace FreeSql.Internal { var readExpValueParms = new List(); var readExpsIndex = Expression.Variable(typeof(int), "readsIndex"); var tryidxExp = Expression.Variable(typeof(int), "tryidx"); + var indexesLengthExp = Expression.Parameter(typeof(int), "indexesLength"); var blockExp = new List(); var ctor = type.GetConstructor(new Type[0]) ?? type.GetConstructors().First(); var ctorParms = ctor.GetParameters(); if (ctorParms.Length > 0) { - foreach(var ctorParm in ctorParms) { + foreach (var ctorParm in ctorParms) { Expression readExpAssign = null; //加速缓存 if (ctorParm.ParameterType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), + GetDataReaderValueBlockExpression(ctorParm.ParameterType, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { @@ -305,12 +314,13 @@ namespace FreeSql.Internal { if (proptypeGeneric.FullName.StartsWith("System.Nullable`1[")) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First(); if (proptypeGeneric.IsEnum || dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), - Expression.Add(dataIndexExp, Expression.Constant(1)) + GetDataReaderValueBlockExpression(ctorParm.ParameterType, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) }), + Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { readExpAssign = Expression.New(RowInfo.Constructor, - Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(ctorParm.ParameterType), namesExp, rowExp, dataIndexExp }), RowInfo.PropertyValue), + Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(ctorParm.ParameterType), indexesExp, rowExp, dataIndexExp }), RowInfo.PropertyValue), Expression.Add(dataIndexExp, Expression.Constant(1))); } } @@ -318,73 +328,74 @@ namespace FreeSql.Internal { readExpValueParms.Add(varctorParm); blockExp.AddRange(new Expression[] { Expression.Assign(tryidxExp, dataIndexExp), - //以下注释部分为【严格读取】,会损失一点性能 - //Expression.IfThen(Expression.Not(Expression.And( - // Expression.NotEqual(namesExp, Expression.Constant(null)), - // Expression.Not(Expression.Call(namesExp, namesExp.Type.GetMethod("TryGetValue"), Expression.Constant(prop.Name), tryidxExp)))), - // Expression.Block( - Expression.Assign(readExp, readExpAssign), - Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), - Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), - Expression.Assign(varctorParm, Expression.Default(ctorParm.ParameterType)), - Expression.Assign(varctorParm, Expression.Convert(readExpValue, ctorParm.ParameterType))) - // ) - //) + Expression.Assign(readExp, readExpAssign), + Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), + Expression.Assign(dataIndexExp, readExpDataIndex)), + Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), + Expression.Assign(varctorParm, Expression.Default(ctorParm.ParameterType)), + Expression.Assign(varctorParm, Expression.Convert(readExpValue, ctorParm.ParameterType))) }); } blockExp.Add(Expression.Assign(retExp, Expression.New(ctor, readExpValueParms))); } else { - blockExp.Add(Expression.Assign(retExp, Expression.New(ctor))); - - var props = type.GetProperties(); + blockExp.AddRange(new Expression[] { + Expression.Assign(retExp, Expression.New(ctor)), + Expression.Assign(indexesLengthExp, Expression.Constant(0)), + Expression.IfThen( + Expression.NotEqual(indexesExp, Expression.Constant(null)), + Expression.Assign(indexesLengthExp, Expression.ArrayLength(indexesExp)) + ) + }); + + var props = type.GetProperties();//.ToDictionary(a => a.Name, a => a, StringComparer.CurrentCultureIgnoreCase); + var propIndex = 0; foreach (var prop in props) { var propGetSetMethod = prop.GetSetMethod(); Expression readExpAssign = null; //加速缓存 if (prop.PropertyType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), - Expression.Add(dataIndexExp, Expression.Constant(1)) + GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp) }), + Expression.Add(tryidxExp, Expression.Constant(1)) ); else { var proptypeGeneric = prop.PropertyType; if (proptypeGeneric.FullName.StartsWith("System.Nullable`1[")) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First(); if (proptypeGeneric.IsEnum || dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(RowInfo.Constructor, - Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp) /*Expression.ArrayAccess(rowExp, dataIndexExp)*/ }), - Expression.Add(dataIndexExp, Expression.Constant(1)) + GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp)), + //Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp) }), + Expression.Add(tryidxExp, Expression.Constant(1)) ); else { continue; - readExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), namesExp, rowExp, tryidxExp }); + //readExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), indexesExp, rowExp, tryidxExp }); } } blockExp.AddRange(new Expression[] { - Expression.Assign(tryidxExp, dataIndexExp), - //以下注释部分为【严格读取】,会损失一点性能 - //Expression.IfThen(Expression.Not(Expression.And( - // Expression.NotEqual(namesExp, Expression.Constant(null)), - // Expression.Not(Expression.Call(namesExp, namesExp.Type.GetMethod("TryGetValue"), Expression.Constant(prop.Name), tryidxExp)))), - // Expression.Block( - Expression.Assign(readExp, readExpAssign), - Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), - Expression.Assign(dataIndexExp, readExpDataIndex)), - Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), - Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), - Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) - // ) - //) + //以下注释部分为【严格读取】,会损失一点性能,使用 select * from xxx 与属性映射赋值 + Expression.IfThenElse( + Expression.LessThan(Expression.Constant(propIndex), indexesLengthExp), + Expression.Assign(tryidxExp, Expression.ArrayAccess(indexesExp, Expression.Constant(propIndex))), + Expression.Assign(tryidxExp, dataIndexExp) + ), + Expression.Assign(readExp, readExpAssign), + Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), + Expression.Assign(dataIndexExp, readExpDataIndex)), + Expression.IfThenElse(Expression.Equal(readExpValue, Expression.Constant(null)), + Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)), + Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType))) }); + ++propIndex; } } blockExp.AddRange(new Expression[] { Expression.Return(returnTarget, Expression.New(RowInfo.Constructor, retExp, dataIndexExp)), Expression.Label(returnTarget, Expression.Default(typeof(RowInfo))) }); - return Expression.Lambda, DbDataReader, int, RowInfo>>( - Expression.Block(new[] { retExp, readExp, tryidxExp, readExpsIndex }.Concat(readExpValueParms), blockExp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile(); + return Expression.Lambda>( + Expression.Block(new[] { retExp, readExp, tryidxExp, readExpsIndex, indexesLengthExp }.Concat(readExpValueParms), blockExp), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile(); }); - - return func(type, names, row, dataIndex); + return func(type, indexes, row, dataIndex); } internal static MethodInfo MethodExecuteArrayRowReadClassOrTuple = typeof(Utils).GetMethod("ExecuteArrayRowReadClassOrTuple", BindingFlags.Static | BindingFlags.NonPublic); @@ -412,6 +423,7 @@ namespace FreeSql.Internal { static ConcurrentDictionary>> _dicGetDataReaderValue = new ConcurrentDictionary>>(); static MethodInfo MethodArrayGetValue = typeof(Array).GetMethod("GetValue", new[] { typeof(int) }); + static MethodInfo MethodArrayGetLength = typeof(Array).GetMethod("GetLength", new[] { typeof(int) }); static MethodInfo MethodMygisGeometryParse = typeof(MygisGeometry).GetMethod("Parse", new[] { typeof(string) }); static MethodInfo MethodGuidParse = typeof(Guid).GetMethod("Parse", new[] { typeof(string) }); static MethodInfo MethodEnumParse = typeof(Enum).GetMethod("Parse", new[] { typeof(Type), typeof(string), typeof(bool) }); @@ -419,133 +431,229 @@ namespace FreeSql.Internal { static MethodInfo MethodConvertChangeType = typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }); static MethodInfo MethodTimeSpanFromSeconds = typeof(TimeSpan).GetMethod("FromSeconds"); static MethodInfo MethodDoubleParse = typeof(double).GetMethod("Parse", new[] { typeof(string) }); - internal static object GetDataReaderValue(Type type, object value) { - if (value == null || value == DBNull.Value) return null; - - var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary>()).GetOrAdd(value.GetType(), valueType => { - var returnTarget = Expression.Label(typeof(object)); - var parmExp = Expression.Parameter(typeof(object), "value"); - - if (type.FullName == "System.Byte[]") return Expression.Lambda>(parmExp, parmExp).Compile(); - + static MethodInfo MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) }); + static MethodInfo MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) }); + static MethodInfo MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) }); + internal static Expression GetDataReaderValueBlockExpression(Type type, Expression value) { + var returnTarget = Expression.Label(typeof(object)); + var valueExp = Expression.Variable(typeof(object), "locvalue"); + Func funcGetExpression = () => { + if (type.FullName == "System.Byte[]") return Expression.Return(returnTarget, valueExp); if (type.IsArray) { var elementType = type.GetElementType(); - if (elementType == valueType.GetElementType()) return Expression.Lambda>(parmExp, parmExp).Compile(); - - var ret = Expression.Variable(type, "ret"); - var arr = Expression.Variable(valueType, "arr"); - var arrlen = Expression.Variable(typeof(int), "arrlen"); - var x = Expression.Variable(typeof(int), "x"); - var readval = Expression.Variable(typeof(object), "readval"); + var arrNewExp = Expression.Variable(type, "arrNew"); + var arrExp = Expression.Variable(typeof(Array), "arr"); + var arrLenExp = Expression.Variable(typeof(int), "arrLen"); + var arrXExp = Expression.Variable(typeof(int), "arrX"); + var arrReadValExp = Expression.Variable(typeof(object), "arrReadVal"); var label = Expression.Label(typeof(int)); - return Expression.Lambda>( + return Expression.IfThenElse( + Expression.TypeEqual(valueExp, type), + Expression.Return(returnTarget, valueExp), Expression.Block( - new[] { ret, arr, arrlen, readval, x }, - Expression.Assign(arr, Expression.TypeAs(parmExp, valueType)), - Expression.Assign(arrlen, Expression.ArrayLength(arr)), - Expression.Assign(x, Expression.Constant(0)), - Expression.Assign(ret, Expression.NewArrayBounds(elementType, arrlen)), + new[] { arrNewExp, arrExp, arrLenExp, arrXExp, arrReadValExp }, + Expression.Assign(arrExp, Expression.TypeAs(valueExp, typeof(Array))), + Expression.Assign(arrLenExp, Expression.Call(arrExp, MethodArrayGetLength, Expression.Constant(0))), + Expression.Assign(arrXExp, Expression.Constant(0)), + Expression.Assign(arrNewExp, Expression.NewArrayBounds(elementType, arrLenExp)), Expression.Loop( Expression.IfThenElse( - Expression.LessThan(x, arrlen), + Expression.LessThan(arrXExp, arrLenExp), Expression.Block( - Expression.Assign(readval, Expression.Call( - MethodGetDataReaderValue, - Expression.Constant(elementType, typeof(Type)), - Expression.Convert(Expression.ArrayAccess(arr, x), typeof(object)) - )), + Expression.Assign(arrReadValExp, GetDataReaderValueBlockExpression(elementType, Expression.Call(arrExp, MethodArrayGetValue, arrXExp))), Expression.IfThenElse( - Expression.Equal(readval, Expression.Constant(null)), - Expression.Assign(Expression.ArrayAccess(ret, x), Expression.Default(elementType)), - Expression.Assign(Expression.ArrayAccess(ret, x), Expression.Convert(readval, elementType)) + Expression.Equal(arrReadValExp, Expression.Constant(null)), + Expression.Assign(Expression.ArrayAccess(arrNewExp, arrXExp), Expression.Default(elementType)), + Expression.Assign(Expression.ArrayAccess(arrNewExp, arrXExp), Expression.Convert(arrReadValExp, elementType)) ), - Expression.PostIncrementAssign(x) + Expression.PostIncrementAssign(arrXExp) ), - Expression.Break(label, x) + Expression.Break(label, arrXExp) ), label ), - Expression.Return(returnTarget, ret), - Expression.Label(returnTarget, Expression.Default(typeof(object))) - ), parmExp).Compile(); + Expression.Return(returnTarget, arrNewExp) + ) + ); } - if (type.FullName.StartsWith("System.Nullable`1[")) type = type.GenericTypeArguments.First(); - if (type.IsEnum) return Expression.Lambda>( - Expression.Call( - MethodEnumParse, - Expression.Constant(type, typeof(Type)), - Expression.Call(MethodToString, parmExp), - Expression.Constant(true, typeof(bool)) - ) , parmExp).Compile(); - - switch (type.FullName) { - case "System.Guid": - if (valueType != type) return Expression.Lambda>( - Expression.Convert(Expression.Call(MethodGuidParse, Expression.Convert(parmExp, typeof(string))), typeof(object)) - , parmExp).Compile(); - return Expression.Lambda>(parmExp, parmExp).Compile(); - - case "MygisPoint": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), - typeof(MygisPoint) - ), parmExp).Compile(); - case "MygisLineString": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), - typeof(MygisLineString) - ), parmExp).Compile(); - case "MygisPolygon": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), - typeof(MygisPolygon) - ), parmExp).Compile(); - case "MygisMultiPoint": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), - typeof(MygisMultiPoint) - ), parmExp).Compile(); - case "MygisMultiLineString": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), - typeof(MygisMultiLineString) - ), parmExp).Compile(); - case "MygisMultiPolygon": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), - typeof(MygisMultiPolygon) - ), parmExp).Compile(); - case "Newtonsoft.Json.Linq.JToken": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(typeof(JToken).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))), - typeof(JToken) - ), parmExp).Compile(); - case "Newtonsoft.Json.Linq.JObject": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(typeof(JObject).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))), - typeof(JObject) - ), parmExp).Compile(); - case "Newtonsoft.Json.Linq.JArray": return Expression.Lambda>( - Expression.TypeAs( - Expression.Call(typeof(JArray).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))), - typeof(JArray) - ), parmExp).Compile(); - case "Npgsql.LegacyPostgis.PostgisGeometry": return Expression.Lambda>(parmExp, parmExp).Compile(); + if (type.IsEnum) return Expression.Return(returnTarget, Expression.Call(MethodEnumParse, Expression.Constant(type, typeof(Type)), Expression.Call(MethodToString, valueExp), Expression.Constant(true, typeof(bool)))); + switch(type.FullName) { + case "System.Guid": return Expression.IfThenElse( + Expression.TypeEqual(valueExp, type), + Expression.Return(returnTarget, valueExp), + Expression.Return(returnTarget, Expression.Convert(Expression.Call(MethodGuidParse, Expression.Convert(valueExp, typeof(string))), typeof(object))) + ); + case "MygisPoint": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisPoint))); + case "MygisLineString": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisLineString))); + case "MygisPolygon": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisPolygon))); + case "MygisMultiPoint": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisMultiPoint))); + case "MygisMultiLineString": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisMultiLineString))); + case "MygisMultiPolygon": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodMygisGeometryParse, Expression.Convert(valueExp, typeof(string))), typeof(MygisMultiPolygon))); + case "Newtonsoft.Json.Linq.JToken": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJTokenParse, Expression.Convert(valueExp, typeof(string))), typeof(JToken))); + case "Newtonsoft.Json.Linq.JObject": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJObjectParse, Expression.Convert(valueExp, typeof(string))), typeof(JObject))); + case "Newtonsoft.Json.Linq.JArray": return Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJArrayParse, Expression.Convert(valueExp, typeof(string))), typeof(JArray))); + case "Npgsql.LegacyPostgis.PostgisGeometry": return Expression.Return(returnTarget, valueExp); + case "System.TimeSpan": return Expression.IfThenElse( + Expression.TypeEqual(valueExp, type), + Expression.Return(returnTarget, valueExp), + Expression.Return(returnTarget, Expression.Convert(Expression.Call(MethodTimeSpanFromSeconds, Expression.Call(MethodDoubleParse, Expression.Call(MethodToString, valueExp))), typeof(object))) + ); } - if (type != valueType) { - if (type.FullName == "System.TimeSpan") return Expression.Lambda>( - Expression.Convert(Expression.Call( - MethodTimeSpanFromSeconds, - Expression.Call(MethodDoubleParse, Expression.Call(MethodToString, parmExp)) - ), typeof(object)), parmExp).Compile(); - return Expression.Lambda>( - Expression.Call(MethodConvertChangeType, parmExp, Expression.Constant(type, typeof(Type))) - , parmExp).Compile(); - } - return Expression.Lambda>(parmExp, parmExp).Compile(); + return Expression.IfThenElse( + Expression.TypeEqual(valueExp, type), + Expression.Return(returnTarget, valueExp), + Expression.Return(returnTarget, Expression.Call(MethodConvertChangeType, valueExp, Expression.Constant(type, typeof(Type)))) + ); + }; + + return Expression.Block( + new[] { valueExp }, + Expression.Assign(valueExp, value), + Expression.IfThenElse( + Expression.Or( + Expression.Equal(valueExp, Expression.Constant(null)), + Expression.Equal(valueExp, Expression.Constant(DBNull.Value)) + ), + Expression.Return(returnTarget, Expression.Convert(Expression.Default(type), typeof(object))), + funcGetExpression() + ), + Expression.Label(returnTarget, Expression.Default(typeof(object))) + ); + } + internal static object GetDataReaderValue(Type type, object value) { + if (value == null || value == DBNull.Value) return null; + var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary>()).GetOrAdd(value.GetType(), valueType => { + var parmExp = Expression.Parameter(typeof(object), "value"); + var exp = GetDataReaderValueBlockExpression(type, parmExp); + return Expression.Lambda>(exp, parmExp).Compile(); }); return func(value); + + //var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary>()).GetOrAdd(value.GetType(), valueType => { + // var returnTarget = Expression.Label(typeof(object)); + // var parmExp = Expression.Parameter(typeof(object), "value"); + + // if (type.FullName == "System.Byte[]") return Expression.Lambda>(parmExp, parmExp).Compile(); + + // if (type.IsArray) { + // var elementType = type.GetElementType(); + // if (elementType == valueType.GetElementType()) return Expression.Lambda>(parmExp, parmExp).Compile(); + + // var ret = Expression.Variable(type, "ret"); + // var arr = Expression.Variable(valueType, "arr"); + // var arrlen = Expression.Variable(typeof(int), "arrlen"); + // var x = Expression.Variable(typeof(int), "x"); + // var readval = Expression.Variable(typeof(object), "readval"); + // var label = Expression.Label(typeof(int)); + // return Expression.Lambda>( + // Expression.Block( + // new[] { ret, arr, arrlen, readval, x }, + // Expression.Assign(arr, Expression.TypeAs(parmExp, valueType)), + // Expression.Assign(arrlen, Expression.ArrayLength(arr)), + // Expression.Assign(x, Expression.Constant(0)), + // Expression.Assign(ret, Expression.NewArrayBounds(elementType, arrlen)), + // Expression.Loop( + // Expression.IfThenElse( + // Expression.LessThan(x, arrlen), + // Expression.Block( + // Expression.Assign(readval, Expression.Call( + // MethodGetDataReaderValue, + // Expression.Constant(elementType, typeof(Type)), + // Expression.Convert(Expression.ArrayAccess(arr, x), typeof(object)) + // )), + // Expression.IfThenElse( + // Expression.Equal(readval, Expression.Constant(null)), + // Expression.Assign(Expression.ArrayAccess(ret, x), Expression.Default(elementType)), + // Expression.Assign(Expression.ArrayAccess(ret, x), Expression.Convert(readval, elementType)) + // ), + // Expression.PostIncrementAssign(x) + // ), + // Expression.Break(label, x) + // ), + // label + // ), + // Expression.Return(returnTarget, ret), + // Expression.Label(returnTarget, Expression.Default(typeof(object))) + // ), parmExp).Compile(); + // } + + // if (type.FullName.StartsWith("System.Nullable`1[")) type = type.GenericTypeArguments.First(); + // if (type.IsEnum) return Expression.Lambda>( + // Expression.Call( + // MethodEnumParse, + // Expression.Constant(type, typeof(Type)), + // Expression.Call(MethodToString, parmExp), + // Expression.Constant(true, typeof(bool)) + // ) , parmExp).Compile(); + + // switch (type.FullName) { + // case "System.Guid": + // if (valueType != type) return Expression.Lambda>( + // Expression.Convert(Expression.Call(MethodGuidParse, Expression.Convert(parmExp, typeof(string))), typeof(object)) + // , parmExp).Compile(); + // return Expression.Lambda>(parmExp, parmExp).Compile(); + + // case "MygisPoint": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), + // typeof(MygisPoint) + // ), parmExp).Compile(); + // case "MygisLineString": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), + // typeof(MygisLineString) + // ), parmExp).Compile(); + // case "MygisPolygon": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), + // typeof(MygisPolygon) + // ), parmExp).Compile(); + // case "MygisMultiPoint": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), + // typeof(MygisMultiPoint) + // ), parmExp).Compile(); + // case "MygisMultiLineString": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), + // typeof(MygisMultiLineString) + // ), parmExp).Compile(); + // case "MygisMultiPolygon": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), + // typeof(MygisMultiPolygon) + // ), parmExp).Compile(); + // case "Newtonsoft.Json.Linq.JToken": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(typeof(JToken).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))), + // typeof(JToken) + // ), parmExp).Compile(); + // case "Newtonsoft.Json.Linq.JObject": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(typeof(JObject).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))), + // typeof(JObject) + // ), parmExp).Compile(); + // case "Newtonsoft.Json.Linq.JArray": return Expression.Lambda>( + // Expression.TypeAs( + // Expression.Call(typeof(JArray).GetMethod("Parse", new[] { typeof(string) }), Expression.Convert(parmExp, typeof(string))), + // typeof(JArray) + // ), parmExp).Compile(); + // case "Npgsql.LegacyPostgis.PostgisGeometry": return Expression.Lambda>(parmExp, parmExp).Compile(); + // } + // if (type != valueType) { + // if (type.FullName == "System.TimeSpan") return Expression.Lambda>( + // Expression.Convert(Expression.Call( + // MethodTimeSpanFromSeconds, + // Expression.Call(MethodDoubleParse, Expression.Call(MethodToString, parmExp)) + // ), typeof(object)), parmExp).Compile(); + // return Expression.Lambda>( + // Expression.Call(MethodConvertChangeType, parmExp, Expression.Constant(type, typeof(Type))) + // , parmExp).Compile(); + // } + // return Expression.Lambda>(parmExp, parmExp).Compile(); + //}); + //return func(value); } internal static object GetDataReaderValue22(Type type, object value) { if (value == null || value == DBNull.Value) return null; @@ -558,7 +666,7 @@ namespace FreeSql.Internal { var ret = Array.CreateInstance(elementType, len); for (var a = 0; a < len; a++) { var item = valueArr.GetValue(a); - ret.SetValue(GetDataReaderValue(elementType, item), a); + ret.SetValue(GetDataReaderValue22(elementType, item), a); } return ret; } diff --git a/FreeSql/PostgreSQL/PostgreSQLExpression.cs b/FreeSql/PostgreSQL/PostgreSQLExpression.cs index 30a1e0f8..5518e66d 100644 --- a/FreeSql/PostgreSQL/PostgreSQLExpression.cs +++ b/FreeSql/PostgreSQL/PostgreSQLExpression.cs @@ -16,7 +16,7 @@ namespace FreeSql.PostgreSQL { Func getExp = exparg => ExpressionLambdaToSql(exparg, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName); switch (exp.NodeType) { case ExpressionType.ArrayLength: - var arrOperExp = getExp((exp as UnaryExpression)); + var arrOperExp = getExp((exp as UnaryExpression).Operand); if (arrOperExp.StartsWith("(") || arrOperExp.EndsWith(")")) return $"array_length(array[{arrOperExp.TrimStart('(').TrimEnd(')')}],1)"; return $"case when {arrOperExp} is null then 0 else array_length({arrOperExp},1) end"; case ExpressionType.Call: