mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	ExpressionTree 优化告一段落
This commit is contained in:
		@@ -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<Type, Dictionary<string, PropertyInfo>> dicQueryTypeGetProperties = new ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>>();
 | 
			
		||||
		public List<T> Query<T>(string cmdText, object parms = null) => Query<T>(CommandType.Text, cmdText, GetDbParamtersByObject(cmdText, parms));
 | 
			
		||||
		public List<T> Query<T>(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
 | 
			
		||||
			var names = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
			var ret = new List<T>();
 | 
			
		||||
			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<int>();
 | 
			
		||||
					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;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,18 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
	partial class AdoProvider {
 | 
			
		||||
		public Task<List<T>> QueryAsync<T>(string cmdText, object parms = null) => QueryAsync<T>(CommandType.Text, cmdText, GetDbParamtersByObject(cmdText, parms));
 | 
			
		||||
		async public Task<List<T>> QueryAsync<T>(CommandType cmdType, string cmdText, params DbParameter[] cmdParms) {
 | 
			
		||||
			var names = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
 | 
			
		||||
			var ret = new List<T>();
 | 
			
		||||
			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<int>();
 | 
			
		||||
					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;
 | 
			
		||||
 
 | 
			
		||||
@@ -271,6 +271,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
				var dicfield = new Dictionary<string, bool>();
 | 
			
		||||
				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<string, int>)), 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[] {
 | 
			
		||||
 
 | 
			
		||||
@@ -24,77 +24,78 @@ namespace FreeSql.Internal {
 | 
			
		||||
		static ConcurrentDictionary<string, TableInfo> _cacheGetTableByEntity = new ConcurrentDictionary<string, TableInfo>();
 | 
			
		||||
		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<T>(string sql, object obj, string paramPrefix, Func<string, Type, object, T> constructorParamter) {
 | 
			
		||||
@@ -166,7 +167,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
			[typeof(JObject)] = true,
 | 
			
		||||
			[typeof(JArray)] = true,
 | 
			
		||||
		};
 | 
			
		||||
		static ConcurrentDictionary<Type, Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary<Type, Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>();
 | 
			
		||||
		internal static ConcurrentDictionary<Type, Func<Type, int[], DbDataReader, int, RowInfo>> _dicExecuteArrayRowReadClassOrTuple = new ConcurrentDictionary<Type, Func<Type, int[], DbDataReader, int, RowInfo>>();
 | 
			
		||||
		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<string, int> 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<string, int>), "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<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
 | 
			
		||||
				if (type.IsArray) return Expression.Lambda<Func<Type, int[], DbDataReader, int, RowInfo>>(
 | 
			
		||||
					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<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
 | 
			
		||||
					return Expression.Lambda<Func<Type, int[], DbDataReader, int, RowInfo>>(
 | 
			
		||||
					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<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
 | 
			
		||||
							Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
 | 
			
		||||
						return Expression.Lambda<Func<Type, int[], DbDataReader, int, RowInfo>>(
 | 
			
		||||
							Expression.Block(new[] { ret2Exp, read2Exp }, block2Exp), new[] { typeExp, indexesExp, rowExp, dataIndexExp }).Compile();
 | 
			
		||||
					}
 | 
			
		||||
					var rowLenExp = Expression.ArrayLength(rowExp);
 | 
			
		||||
					return Expression.Lambda<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
 | 
			
		||||
					return Expression.Lambda<Func<Type, int[], DbDataReader, int, RowInfo>>(
 | 
			
		||||
						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<Type, Dictionary<string, int>, DbDataReader, int, RowInfo> dynamicFunc = (type2, names2, row2, dataindex2) => {
 | 
			
		||||
				if (type == typeof(object) && indexes != null) {
 | 
			
		||||
					Func<Type, int[], DbDataReader, int, RowInfo> dynamicFunc = (type2, indexes2, row2, dataindex2) => {
 | 
			
		||||
						dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写
 | 
			
		||||
						var expandodic = (IDictionary<string, object>)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<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(null);
 | 
			
		||||
					return dynamicFunc;// Expression.Lambda<Func<Type, int[], DbDataReader, int, RowInfo>>(null);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				//类注入属性
 | 
			
		||||
@@ -290,14 +297,16 @@ namespace FreeSql.Internal {
 | 
			
		||||
				var readExpValueParms = new List<ParameterExpression>();
 | 
			
		||||
				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<Expression>();
 | 
			
		||||
				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<Func<Type, Dictionary<string, int>, DbDataReader, int, RowInfo>>(
 | 
			
		||||
					Expression.Block(new[] { retExp, readExp, tryidxExp, readExpsIndex }.Concat(readExpValueParms), blockExp), new[] { typeExp, namesExp, rowExp, dataIndexExp }).Compile();
 | 
			
		||||
				return Expression.Lambda<Func<Type, int[], DbDataReader, int, RowInfo>>(
 | 
			
		||||
					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<Type, ConcurrentDictionary<Type, Func<object, object>>> _dicGetDataReaderValue = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Func<object, object>>>();
 | 
			
		||||
		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<Type, Func<object, object>>()).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<Func<object, object>>(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<Expression> 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<Func<object, object>>(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<Func<object, object>>(
 | 
			
		||||
					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<Func<object, object>>(
 | 
			
		||||
					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<Func<object, object>>(
 | 
			
		||||
							Expression.Convert(Expression.Call(MethodGuidParse, Expression.Convert(parmExp, typeof(string))), typeof(object))
 | 
			
		||||
							, parmExp).Compile();
 | 
			
		||||
						return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
 | 
			
		||||
 | 
			
		||||
					case "MygisPoint": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							Expression.TypeAs(
 | 
			
		||||
								Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
 | 
			
		||||
								typeof(MygisPoint)
 | 
			
		||||
							), parmExp).Compile();
 | 
			
		||||
					case "MygisLineString": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							Expression.TypeAs(
 | 
			
		||||
								Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
								typeof(MygisLineString)
 | 
			
		||||
							), parmExp).Compile();
 | 
			
		||||
					case "MygisPolygon": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							Expression.TypeAs(
 | 
			
		||||
								Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
								typeof(MygisPolygon)
 | 
			
		||||
							), parmExp).Compile();
 | 
			
		||||
					case "MygisMultiPoint": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							Expression.TypeAs(
 | 
			
		||||
								Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
								typeof(MygisMultiPoint)
 | 
			
		||||
							), parmExp).Compile();
 | 
			
		||||
					case "MygisMultiLineString": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							Expression.TypeAs(
 | 
			
		||||
								Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
								typeof(MygisMultiLineString)
 | 
			
		||||
							), parmExp).Compile();
 | 
			
		||||
					case "MygisMultiPolygon": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							Expression.TypeAs(
 | 
			
		||||
								Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
								typeof(MygisMultiPolygon)
 | 
			
		||||
							), parmExp).Compile();
 | 
			
		||||
					case "Newtonsoft.Json.Linq.JToken": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
							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<Func<object, object>>(
 | 
			
		||||
							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<Func<object, object>>(
 | 
			
		||||
							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<Func<object, object>>(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<Func<object, object>>(
 | 
			
		||||
						Expression.Convert(Expression.Call(
 | 
			
		||||
							MethodTimeSpanFromSeconds,
 | 
			
		||||
							Expression.Call(MethodDoubleParse, Expression.Call(MethodToString, parmExp))
 | 
			
		||||
						), typeof(object)), parmExp).Compile();
 | 
			
		||||
					return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
						Expression.Call(MethodConvertChangeType, parmExp, Expression.Constant(type, typeof(Type)))
 | 
			
		||||
					, parmExp).Compile();
 | 
			
		||||
				}
 | 
			
		||||
				return Expression.Lambda<Func<object, object>>(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<Type, Func<object, object>>()).GetOrAdd(value.GetType(), valueType => {
 | 
			
		||||
				var parmExp = Expression.Parameter(typeof(object), "value");
 | 
			
		||||
				var exp = GetDataReaderValueBlockExpression(type, parmExp);
 | 
			
		||||
				return Expression.Lambda<Func<object, object>>(exp, parmExp).Compile();
 | 
			
		||||
			});
 | 
			
		||||
			return func(value);
 | 
			
		||||
 | 
			
		||||
			//var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary<Type, Func<object, object>>()).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<Func<object, object>>(parmExp, parmExp).Compile();
 | 
			
		||||
 | 
			
		||||
			//	if (type.IsArray) {
 | 
			
		||||
			//		var elementType = type.GetElementType();
 | 
			
		||||
			//		if (elementType == valueType.GetElementType()) return Expression.Lambda<Func<object, object>>(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<Func<object, object>>(
 | 
			
		||||
			//			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<Func<object, object>>(
 | 
			
		||||
			//		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<Func<object, object>>(
 | 
			
		||||
			//				Expression.Convert(Expression.Call(MethodGuidParse, Expression.Convert(parmExp, typeof(string))), typeof(object))
 | 
			
		||||
			//				, parmExp).Compile();
 | 
			
		||||
			//			return Expression.Lambda<Func<object, object>>(parmExp, parmExp).Compile();
 | 
			
		||||
 | 
			
		||||
			//		case "MygisPoint": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				Expression.TypeAs(
 | 
			
		||||
			//					Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))),
 | 
			
		||||
			//					typeof(MygisPoint)
 | 
			
		||||
			//				), parmExp).Compile();
 | 
			
		||||
			//		case "MygisLineString": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				Expression.TypeAs(
 | 
			
		||||
			//					Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
			//					typeof(MygisLineString)
 | 
			
		||||
			//				), parmExp).Compile();
 | 
			
		||||
			//		case "MygisPolygon": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				Expression.TypeAs(
 | 
			
		||||
			//					Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
			//					typeof(MygisPolygon)
 | 
			
		||||
			//				), parmExp).Compile();
 | 
			
		||||
			//		case "MygisMultiPoint": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				Expression.TypeAs(
 | 
			
		||||
			//					Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
			//					typeof(MygisMultiPoint)
 | 
			
		||||
			//				), parmExp).Compile();
 | 
			
		||||
			//		case "MygisMultiLineString": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				Expression.TypeAs(
 | 
			
		||||
			//					Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
			//					typeof(MygisMultiLineString)
 | 
			
		||||
			//				), parmExp).Compile();
 | 
			
		||||
			//		case "MygisMultiPolygon": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				Expression.TypeAs(
 | 
			
		||||
			//					Expression.Call(MethodMygisGeometryParse, Expression.Convert(parmExp, typeof(string))), 
 | 
			
		||||
			//					typeof(MygisMultiPolygon)
 | 
			
		||||
			//				), parmExp).Compile();
 | 
			
		||||
			//		case "Newtonsoft.Json.Linq.JToken": return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//				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<Func<object, object>>(
 | 
			
		||||
			//				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<Func<object, object>>(
 | 
			
		||||
			//				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<Func<object, object>>(parmExp, parmExp).Compile();
 | 
			
		||||
			//	}
 | 
			
		||||
			//	if (type != valueType) {
 | 
			
		||||
			//		if (type.FullName == "System.TimeSpan") return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//			Expression.Convert(Expression.Call(
 | 
			
		||||
			//				MethodTimeSpanFromSeconds,
 | 
			
		||||
			//				Expression.Call(MethodDoubleParse, Expression.Call(MethodToString, parmExp))
 | 
			
		||||
			//			), typeof(object)), parmExp).Compile();
 | 
			
		||||
			//		return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
			//			Expression.Call(MethodConvertChangeType, parmExp, Expression.Constant(type, typeof(Type)))
 | 
			
		||||
			//		, parmExp).Compile();
 | 
			
		||||
			//	}
 | 
			
		||||
			//	return Expression.Lambda<Func<object, object>>(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;
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user