mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 Column.MapType 类型映射,可将 enum 映射为 int/string 等;
This commit is contained in:
		@@ -13,18 +13,21 @@ namespace FreeSql.Internal {
 | 
			
		||||
	internal abstract class CommonExpression {
 | 
			
		||||
 | 
			
		||||
		internal CommonUtils _common;
 | 
			
		||||
		internal CommonProvider.AdoProvider _ado => _adoPriv ?? (_adoPriv = _common._orm.Ado as CommonProvider.AdoProvider);
 | 
			
		||||
		CommonProvider.AdoProvider _adoPriv;
 | 
			
		||||
		internal CommonExpression(CommonUtils common) {
 | 
			
		||||
			_common = common;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static ConcurrentDictionary<Type, PropertyInfo[]> _dicReadAnonymousFieldDtoPropertys = new ConcurrentDictionary<Type, PropertyInfo[]>();
 | 
			
		||||
		internal bool ReadAnonymousField(List<SelectTableInfo> _tables, StringBuilder field, ReadAnonymousTypeInfo parent, ref int index, Expression exp, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
			Func<ExpTSC> getTSC = () => new ExpTSC { _tables = _tables, getSelectGroupingMapString = getSelectGroupingMapString, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where };
 | 
			
		||||
			switch (exp.NodeType) {
 | 
			
		||||
				case ExpressionType.Quote: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand, getSelectGroupingMapString);
 | 
			
		||||
				case ExpressionType.Lambda: return ReadAnonymousField(_tables, field, parent, ref index, (exp as LambdaExpression)?.Body, getSelectGroupingMapString);
 | 
			
		||||
				case ExpressionType.Negate:
 | 
			
		||||
				case ExpressionType.NegateChecked:
 | 
			
		||||
					parent.DbField = $"-({ExpressionLambdaToSql(exp, _tables, null, getSelectGroupingMapString, SelectTableInfoType.From, true, false, ExpressionStyle.Where)})";
 | 
			
		||||
					parent.DbField = $"-({ExpressionLambdaToSql(exp, getTSC())})";
 | 
			
		||||
					field.Append(", ").Append(parent.DbField);
 | 
			
		||||
					if (index >= 0) field.Append(" as").Append(++index);
 | 
			
		||||
					return false;
 | 
			
		||||
@@ -51,7 +54,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
						callExp.Arguments[0].Type.FullName == "System.String")
 | 
			
		||||
						parent.DbField = (callExp.Arguments[0] as ConstantExpression).Value?.ToString() ?? "NULL";
 | 
			
		||||
					else
 | 
			
		||||
						parent.DbField = ExpressionLambdaToSql(exp, _tables, null, getSelectGroupingMapString, SelectTableInfoType.From, true, false, ExpressionStyle.Where);
 | 
			
		||||
						parent.DbField = ExpressionLambdaToSql(exp, getTSC());
 | 
			
		||||
					field.Append(", ").Append(parent.DbField);
 | 
			
		||||
					if (index >= 0) field.Append(" as").Append(++index);
 | 
			
		||||
					return false;
 | 
			
		||||
@@ -67,17 +70,19 @@ namespace FreeSql.Internal {
 | 
			
		||||
							var child = new ReadAnonymousTypeInfo {
 | 
			
		||||
								Property = tb.Properties.TryGetValue(map[idx].Column.CsName, out var tryprop) ? tryprop : tb.Type.GetProperty(map[idx].Column.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
 | 
			
		||||
								CsName = map[idx].Column.CsName, DbField = $"{map[idx].Table.Alias}.{_common.QuoteSqlName(map[idx].Column.Attribute.Name)}",
 | 
			
		||||
								CsType = map[idx].Column.CsType
 | 
			
		||||
								CsType = map[idx].Column.CsType,
 | 
			
		||||
								MapType = map[idx].Column.Attribute.MapType
 | 
			
		||||
							};
 | 
			
		||||
							field.Append(", ").Append(_common.QuoteReadColumn(map[idx].Column.CsType, child.DbField));
 | 
			
		||||
							field.Append(", ").Append(_common.QuoteReadColumn(child.MapType, child.DbField));
 | 
			
		||||
							if (index >= 0) field.Append(" as").Append(++index);
 | 
			
		||||
							parent.Childs.Add(child);
 | 
			
		||||
						}
 | 
			
		||||
					} else {
 | 
			
		||||
						parent.CsType = exp.Type;
 | 
			
		||||
						parent.DbField = ExpressionLambdaToSql(exp, _tables, null, getSelectGroupingMapString, SelectTableInfoType.From, true, false, ExpressionStyle.Where);
 | 
			
		||||
						parent.DbField = ExpressionLambdaToSql(exp, getTSC());
 | 
			
		||||
						field.Append(", ").Append(parent.DbField);
 | 
			
		||||
						if (index >= 0) field.Append(" as").Append(++index);
 | 
			
		||||
						parent.MapType = SearchColumnByField(_tables, null, parent.DbField)?.Attribute.MapType ?? exp.Type;
 | 
			
		||||
						return false;
 | 
			
		||||
					}
 | 
			
		||||
					return false;
 | 
			
		||||
@@ -93,7 +98,8 @@ namespace FreeSql.Internal {
 | 
			
		||||
							var child = new ReadAnonymousTypeInfo {
 | 
			
		||||
								Property = initExp.Type.GetProperty(initExp.Bindings[a].Member.Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
 | 
			
		||||
								CsName = initExp.Bindings[a].Member.Name,
 | 
			
		||||
								CsType = initAssignExp.Expression.Type
 | 
			
		||||
								CsType = initAssignExp.Expression.Type,
 | 
			
		||||
								MapType = initAssignExp.Expression.Type
 | 
			
		||||
							};
 | 
			
		||||
							parent.Childs.Add(child);
 | 
			
		||||
							ReadAnonymousField(_tables, field, child, ref index, initAssignExp.Expression, getSelectGroupingMapString);
 | 
			
		||||
@@ -107,7 +113,8 @@ namespace FreeSql.Internal {
 | 
			
		||||
									var child = new ReadAnonymousTypeInfo {
 | 
			
		||||
										Property = dtoProp,
 | 
			
		||||
										CsName = dtoProp.Name,
 | 
			
		||||
										CsType = dtoProp.PropertyType
 | 
			
		||||
										CsType = dtoProp.PropertyType,
 | 
			
		||||
										MapType = trydtocol.Attribute.MapType
 | 
			
		||||
									};
 | 
			
		||||
									parent.Childs.Add(child);
 | 
			
		||||
									if (dtTb.Parameter != null)
 | 
			
		||||
@@ -133,7 +140,8 @@ namespace FreeSql.Internal {
 | 
			
		||||
							var child = new ReadAnonymousTypeInfo {
 | 
			
		||||
								Property = newExp.Type.GetProperty(newExp.Members[a].Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance),
 | 
			
		||||
								CsName = newExp.Members[a].Name,
 | 
			
		||||
								CsType = newExp.Arguments[a].Type
 | 
			
		||||
								CsType = newExp.Arguments[a].Type,
 | 
			
		||||
								MapType = newExp.Arguments[a].Type
 | 
			
		||||
							};
 | 
			
		||||
							parent.Childs.Add(child);
 | 
			
		||||
							ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a], getSelectGroupingMapString);
 | 
			
		||||
@@ -148,7 +156,8 @@ namespace FreeSql.Internal {
 | 
			
		||||
									var child = new ReadAnonymousTypeInfo {
 | 
			
		||||
										Property = dtoProp,
 | 
			
		||||
										CsName = dtoProp.Name,
 | 
			
		||||
										CsType = dtoProp.PropertyType
 | 
			
		||||
										CsType = dtoProp.PropertyType,
 | 
			
		||||
										MapType = trydtocol.Attribute.MapType
 | 
			
		||||
									};
 | 
			
		||||
									parent.Childs.Add(child);
 | 
			
		||||
									if (dtTb.Parameter != null)
 | 
			
		||||
@@ -166,7 +175,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
					}
 | 
			
		||||
					return true;
 | 
			
		||||
			}
 | 
			
		||||
			parent.DbField = $"({ExpressionLambdaToSql(exp, _tables, null, getSelectGroupingMapString, SelectTableInfoType.From, true, false, ExpressionStyle.Where)})";
 | 
			
		||||
			parent.DbField = $"({ExpressionLambdaToSql(exp, getTSC())})";
 | 
			
		||||
			field.Append(", ").Append(parent.DbField);
 | 
			
		||||
			if (index >= 0) field.Append(" as").Append(++index);
 | 
			
		||||
			return false;
 | 
			
		||||
@@ -177,7 +186,9 @@ namespace FreeSql.Internal {
 | 
			
		||||
					++index;
 | 
			
		||||
					return Utils.GetDataReaderValue(parent.CsType, null);
 | 
			
		||||
				}
 | 
			
		||||
				return Utils.GetDataReaderValue(parent.CsType, dr.GetValue(++index));
 | 
			
		||||
				if (parent.CsType == parent.MapType)
 | 
			
		||||
					return Utils.GetDataReaderValue(parent.CsType, dr.GetValue(++index));
 | 
			
		||||
				return Utils.GetDataReaderValue(parent.CsType, Utils.GetDataReaderValue(parent.MapType, dr.GetValue(++index)));
 | 
			
		||||
			}
 | 
			
		||||
			switch (parent.ConsturctorType) {
 | 
			
		||||
				case ReadAnonymousTypeInfoConsturctorType.Arguments:
 | 
			
		||||
@@ -204,10 +215,25 @@ namespace FreeSql.Internal {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal string ExpressionConstant(ConstantExpression exp) => _common.FormatSql("{0}", exp?.Value);
 | 
			
		||||
		internal ColumnInfo SearchColumnByField(List<SelectTableInfo> _tables, TableInfo currentTable, string field) {
 | 
			
		||||
			if (_tables != null) {
 | 
			
		||||
				var testCol = _common.TrimQuoteSqlName(field).Split(new[] { '.' }, 2);
 | 
			
		||||
				if (testCol.Length == 2) {
 | 
			
		||||
					var testTb = _tables.Where(a => a.Alias == testCol[0]).ToArray();
 | 
			
		||||
					if (testTb.Length == 1 && testTb[0].Table.Columns.TryGetValue(testCol[1], out var trytstcol))
 | 
			
		||||
						return trytstcol;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (currentTable != null) {
 | 
			
		||||
				var testCol = _common.TrimQuoteSqlName(field);
 | 
			
		||||
				if (currentTable.Columns.TryGetValue(testCol, out var trytstcol))
 | 
			
		||||
					return trytstcol;
 | 
			
		||||
			}
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal string ExpressionSelectColumn_MemberAccess(List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, SelectTableInfoType tbtype, Expression exp, bool isQuoteName, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
			return ExpressionLambdaToSql(exp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, false, ExpressionStyle.SelectColumns);
 | 
			
		||||
			return ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, _selectColumnMap = _selectColumnMap, getSelectGroupingMapString = getSelectGroupingMapString, tbtype = tbtype, isQuoteName = isQuoteName, isDisableDiyParse = false, style = ExpressionStyle.SelectColumns });
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal string[] ExpressionSelectColumns_MemberAccess_New_NewArrayInit(List<SelectTableInfo> _tables, Expression exp, bool isQuoteName, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
@@ -250,9 +276,9 @@ namespace FreeSql.Internal {
 | 
			
		||||
			{ ExpressionType.Modulo, "%" },
 | 
			
		||||
			{ ExpressionType.Equal, "=" },
 | 
			
		||||
		};
 | 
			
		||||
		internal string ExpressionWhereLambdaNoneForeignObject(List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Expression exp, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
			var sql = ExpressionLambdaToSql(exp, _tables, _selectColumnMap, getSelectGroupingMapString, SelectTableInfoType.From, true, false, ExpressionStyle.Where);
 | 
			
		||||
			switch(sql) {
 | 
			
		||||
		internal string ExpressionWhereLambdaNoneForeignObject(List<SelectTableInfo> _tables, TableInfo table, List<SelectColumnInfo> _selectColumnMap, Expression exp, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
			var sql = ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, _selectColumnMap = _selectColumnMap, getSelectGroupingMapString = getSelectGroupingMapString, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where, currentTable = table });
 | 
			
		||||
			switch (sql) {
 | 
			
		||||
				case "1":
 | 
			
		||||
				case "'t'": return "1=1";
 | 
			
		||||
				case "0":
 | 
			
		||||
@@ -262,7 +288,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal string ExpressionWhereLambda(List<SelectTableInfo> _tables, Expression exp, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
			var sql = ExpressionLambdaToSql(exp, _tables, null, getSelectGroupingMapString, SelectTableInfoType.From, true, false, ExpressionStyle.Where);
 | 
			
		||||
			var sql = ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, getSelectGroupingMapString = getSelectGroupingMapString, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where });
 | 
			
		||||
			switch (sql) {
 | 
			
		||||
				case "1":
 | 
			
		||||
				case "'t'": return "1=1";
 | 
			
		||||
@@ -273,7 +299,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
		}
 | 
			
		||||
		internal void ExpressionJoinLambda(List<SelectTableInfo> _tables, SelectTableInfoType tbtype, Expression exp, Func<Expression[], string> getSelectGroupingMapString) {
 | 
			
		||||
			var tbidx = _tables.Count;
 | 
			
		||||
			var filter = ExpressionLambdaToSql(exp, _tables, null, getSelectGroupingMapString, tbtype, true, false, ExpressionStyle.Where);
 | 
			
		||||
			var filter = ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, getSelectGroupingMapString = getSelectGroupingMapString, tbtype = tbtype, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where });
 | 
			
		||||
			switch (filter) {
 | 
			
		||||
				case "1":
 | 
			
		||||
				case "'t'": filter = "1=1"; break;
 | 
			
		||||
@@ -300,50 +326,80 @@ namespace FreeSql.Internal {
 | 
			
		||||
		static ConcurrentDictionary<Type, MethodInfo> _dicExpressionLambdaToSqlAsSelectAnyMethodInfo = new ConcurrentDictionary<Type, MethodInfo>();
 | 
			
		||||
		static ConcurrentDictionary<Type, PropertyInfo> _dicNullableValueProperty = new ConcurrentDictionary<Type, PropertyInfo>();
 | 
			
		||||
		static ConcurrentDictionary<Type, Expression> _dicFreeSqlGlobalExtensionsAsSelectExpression = new ConcurrentDictionary<Type, Expression>();
 | 
			
		||||
		internal string ExpressionLambdaToSql(Expression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style) {
 | 
			
		||||
 | 
			
		||||
		internal string ExpressionBinary(string oper, Expression leftExp, Expression rightExp, ExpTSC tsc) {
 | 
			
		||||
			var left = ExpressionLambdaToSql(leftExp, tsc);
 | 
			
		||||
			var leftMapColumn = SearchColumnByField(tsc._tables, tsc.currentTable, left);
 | 
			
		||||
			var isLeftMapType = leftMapColumn != null && (leftMapColumn.Attribute.MapType != rightExp.Type || leftMapColumn.CsType != rightExp.Type);
 | 
			
		||||
			ColumnInfo rightMapColumn = null;
 | 
			
		||||
			var isRightMapType = false;
 | 
			
		||||
			if (isLeftMapType) tsc.mapType = leftMapColumn.Attribute.MapType;
 | 
			
		||||
 | 
			
		||||
			var right = ExpressionLambdaToSql(rightExp, tsc);
 | 
			
		||||
			if (right != "NULL" && isLeftMapType) {
 | 
			
		||||
				var enumType = leftMapColumn.CsType.NullableTypeOrThis();
 | 
			
		||||
				if (enumType.IsEnum)
 | 
			
		||||
					right = formatSql(Enum.Parse(enumType, right.Trim('\'')), leftMapColumn.Attribute.MapType);
 | 
			
		||||
			}
 | 
			
		||||
			if (leftMapColumn == null) {
 | 
			
		||||
				rightMapColumn = SearchColumnByField(tsc._tables, tsc.currentTable, right);
 | 
			
		||||
				isRightMapType = rightMapColumn != null && (rightMapColumn.Attribute.MapType != leftExp.Type || rightMapColumn.CsType != leftExp.Type);
 | 
			
		||||
				if (isRightMapType) {
 | 
			
		||||
					tsc.mapType = rightMapColumn.Attribute.MapType;
 | 
			
		||||
					left = ExpressionLambdaToSql(leftExp, tsc);
 | 
			
		||||
					if (left != "NULL" && isRightMapType) {
 | 
			
		||||
						var enumType = rightMapColumn.CsType.NullableTypeOrThis();
 | 
			
		||||
						if (enumType.IsEnum)
 | 
			
		||||
							left = formatSql(Enum.Parse(enumType, left.Trim('\'')), rightMapColumn.Attribute.MapType);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (left == "NULL") {
 | 
			
		||||
				var tmp = right;
 | 
			
		||||
				right = left;
 | 
			
		||||
				left = tmp;
 | 
			
		||||
			}
 | 
			
		||||
			if (right == "NULL") oper = oper == "=" ? " IS " : " IS NOT ";
 | 
			
		||||
			if (oper == "+" && (leftExp.Type.FullName == "System.String" || rightExp.Type.FullName == "System.String")) return _common.StringConcat(new[] { left, right }, new[] { leftExp.Type, rightExp.Type });
 | 
			
		||||
			if (oper == "%") return _common.Mod(left, right, leftExp.Type, rightExp.Type);
 | 
			
		||||
			tsc.mapType = null;
 | 
			
		||||
			return $"{left} {oper} {right}";
 | 
			
		||||
		}
 | 
			
		||||
		internal string ExpressionLambdaToSql(Expression exp, ExpTSC tsc) {
 | 
			
		||||
			if (exp == null) return "";
 | 
			
		||||
			if (isDisableDiyParse == false && _common._orm.Aop.ParseExpression != null) {
 | 
			
		||||
				var args = new AopParseExpressionEventArgs(exp, ukexp => ExpressionLambdaToSql(exp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, true, style));
 | 
			
		||||
			if (tsc.isDisableDiyParse == false && _common._orm.Aop.ParseExpression != null) {
 | 
			
		||||
				var args = new AopParseExpressionEventArgs(exp, ukexp => ExpressionLambdaToSql(exp, tsc.CloneDisableDiyParse()));
 | 
			
		||||
				_common._orm.Aop.ParseExpression?.Invoke(this, args);
 | 
			
		||||
				if (string.IsNullOrEmpty(args.Result) == false) return args.Result;
 | 
			
		||||
			}
 | 
			
		||||
			switch (exp.NodeType) {
 | 
			
		||||
				case ExpressionType.Not: return $"not({ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)})";
 | 
			
		||||
				case ExpressionType.Quote: return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
				case ExpressionType.Lambda: return ExpressionLambdaToSql((exp as LambdaExpression)?.Body, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
				case ExpressionType.Not: return $"not({ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc)})";
 | 
			
		||||
				case ExpressionType.Quote: return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc);
 | 
			
		||||
				case ExpressionType.Lambda: return ExpressionLambdaToSql((exp as LambdaExpression)?.Body, tsc);
 | 
			
		||||
				case ExpressionType.TypeAs:
 | 
			
		||||
				case ExpressionType.Convert:
 | 
			
		||||
					//var othercExp = ExpressionLambdaToSqlOther(exp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
					//var othercExp = ExpressionLambdaToSqlOther(exp, tsc);
 | 
			
		||||
					//if (string.IsNullOrEmpty(othercExp) == false) return othercExp;
 | 
			
		||||
					return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
					return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc);
 | 
			
		||||
				case ExpressionType.Negate:
 | 
			
		||||
				case ExpressionType.NegateChecked: return "-" + ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
				case ExpressionType.Constant: return _common.FormatSql("{0}", (exp as ConstantExpression)?.Value);
 | 
			
		||||
				case ExpressionType.NegateChecked: return "-" + ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc);
 | 
			
		||||
				case ExpressionType.Constant: return formatSql((exp as ConstantExpression)?.Value, tsc.mapType);
 | 
			
		||||
				case ExpressionType.Conditional:
 | 
			
		||||
					var condExp = exp as ConditionalExpression;
 | 
			
		||||
					return $"case when {ExpressionLambdaToSql(condExp.Test, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)} then {ExpressionLambdaToSql(condExp.IfTrue, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)} else {ExpressionLambdaToSql(condExp.IfFalse, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)} end";
 | 
			
		||||
					return $"case when {ExpressionLambdaToSql(condExp.Test, tsc)} then {ExpressionLambdaToSql(condExp.IfTrue, tsc)} else {ExpressionLambdaToSql(condExp.IfFalse, tsc)} end";
 | 
			
		||||
				case ExpressionType.Call:
 | 
			
		||||
					var exp3 = exp as MethodCallExpression;
 | 
			
		||||
					var callType = exp3.Object?.Type ?? exp3.Method.DeclaringType;
 | 
			
		||||
					switch (callType.FullName) {
 | 
			
		||||
						case "System.String": return ExpressionLambdaToSqlCallString(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						case "System.Math": return ExpressionLambdaToSqlCallMath(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						case "System.DateTime": return ExpressionLambdaToSqlCallDateTime(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						case "System.TimeSpan": return ExpressionLambdaToSqlCallTimeSpan(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						case "System.Convert": return ExpressionLambdaToSqlCallConvert(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
					}
 | 
			
		||||
					if (exp3.Method.Name == "Equals" && exp3.Object != null && exp3.Arguments.Count > 0) {
 | 
			
		||||
						var tmptryoper = "=";
 | 
			
		||||
						var tmpleft = ExpressionLambdaToSql(exp3.Object, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						var tmpright = ExpressionLambdaToSql(exp3.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						if (tmpleft == "NULL") {
 | 
			
		||||
							var tmp33 = tmpright;
 | 
			
		||||
							tmpright = tmpleft;
 | 
			
		||||
							tmpleft = tmp33;
 | 
			
		||||
						}
 | 
			
		||||
						if (tmpright == "NULL") tmptryoper = " IS ";
 | 
			
		||||
						return $"{tmpleft} {tmptryoper} {tmpright}";
 | 
			
		||||
						case "System.String": return ExpressionLambdaToSqlCallString(exp3, tsc);
 | 
			
		||||
						case "System.Math": return ExpressionLambdaToSqlCallMath(exp3, tsc);
 | 
			
		||||
						case "System.DateTime": return ExpressionLambdaToSqlCallDateTime(exp3, tsc);
 | 
			
		||||
						case "System.TimeSpan": return ExpressionLambdaToSqlCallTimeSpan(exp3, tsc);
 | 
			
		||||
						case "System.Convert": return ExpressionLambdaToSqlCallConvert(exp3, tsc);
 | 
			
		||||
					}
 | 
			
		||||
					if (exp3.Method.Name == "Equals" && exp3.Object != null && exp3.Arguments.Count > 0)
 | 
			
		||||
						return ExpressionBinary("=", exp3.Object, exp3.Arguments[0], tsc);
 | 
			
		||||
					if (callType.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`")) {
 | 
			
		||||
						//if (exp3.Type == typeof(string) && exp3.Arguments.Any() && exp3.Arguments[0].NodeType == ExpressionType.Constant) {
 | 
			
		||||
						//	switch (exp3.Method.Name) {
 | 
			
		||||
@@ -355,10 +411,10 @@ namespace FreeSql.Internal {
 | 
			
		||||
						//}
 | 
			
		||||
						switch (exp3.Method.Name) {
 | 
			
		||||
							case "Count": return "count(1)";
 | 
			
		||||
							case "Sum": return $"sum({ExpressionLambdaToSql(exp3.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)})";
 | 
			
		||||
							case "Avg": return $"avg({ExpressionLambdaToSql(exp3.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)})";
 | 
			
		||||
							case "Max": return $"max({ExpressionLambdaToSql(exp3.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)})";
 | 
			
		||||
							case "Min": return $"min({ExpressionLambdaToSql(exp3.Arguments[0], _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)})";
 | 
			
		||||
							case "Sum": return $"sum({ExpressionLambdaToSql(exp3.Arguments[0], tsc)})";
 | 
			
		||||
							case "Avg": return $"avg({ExpressionLambdaToSql(exp3.Arguments[0], tsc)})";
 | 
			
		||||
							case "Max": return $"max({ExpressionLambdaToSql(exp3.Arguments[0], tsc)})";
 | 
			
		||||
							case "Min": return $"min({ExpressionLambdaToSql(exp3.Arguments[0], tsc)})";
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if (callType.FullName.StartsWith("FreeSql.ISelect`")) { //子表查询
 | 
			
		||||
@@ -409,7 +465,10 @@ namespace FreeSql.Internal {
 | 
			
		||||
																var testExecuteExp = asSelectParentExp;
 | 
			
		||||
																if (asSelectParentExp.NodeType == ExpressionType.Parameter) //执行leftjoin关联
 | 
			
		||||
																	testExecuteExp = Expression.Property(testExecuteExp, _common.GetTableByEntity(asSelectParentExp.Type).Properties.First().Value);
 | 
			
		||||
																asSelectSql = ExpressionLambdaToSql(testExecuteExp, _tables, new List<SelectColumnInfo>(), getSelectGroupingMapString, SelectTableInfoType.LeftJoin, isQuoteName, true, ExpressionStyle.AsSelect);
 | 
			
		||||
																var tsc2 = tsc.CloneSetgetSelectGroupingMapStringAndgetSelectGroupingMapStringAndtbtype(new List<SelectColumnInfo>(), tsc.getSelectGroupingMapString, SelectTableInfoType.LeftJoin);
 | 
			
		||||
																tsc2.isDisableDiyParse = true;
 | 
			
		||||
																tsc2.style = ExpressionStyle.AsSelect;
 | 
			
		||||
																asSelectSql = ExpressionLambdaToSql(testExecuteExp, tsc2);
 | 
			
		||||
															}
 | 
			
		||||
														}
 | 
			
		||||
													}
 | 
			
		||||
@@ -422,8 +481,8 @@ namespace FreeSql.Internal {
 | 
			
		||||
									if (fsqlType == null) break;
 | 
			
		||||
									fsqlType.GetField("_limit", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(fsql, 1);
 | 
			
		||||
									fsqltables = fsqlType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(fsql) as List<SelectTableInfo>;
 | 
			
		||||
									//fsqltables[0].Alias = $"{_tables[0].Alias}_{fsqltables[0].Alias}";
 | 
			
		||||
									fsqltables.AddRange(_tables.Select(a => new SelectTableInfo {
 | 
			
		||||
									//fsqltables[0].Alias = $"{tsc._tables[0].Alias}_{fsqltables[0].Alias}";
 | 
			
		||||
									fsqltables.AddRange(tsc._tables.Select(a => new SelectTableInfo {
 | 
			
		||||
										Alias = a.Alias,
 | 
			
		||||
										On = "1=1",
 | 
			
		||||
										Table = a.Table,
 | 
			
		||||
@@ -482,7 +541,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
												typeof(FreeSqlGlobalExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public).Where(mfil => mfil.Name == "AsSelect" && mfil.GetParameters().Length == 1).FirstOrDefault()?.MakeGenericMethod(refMiddleEntityType3),
 | 
			
		||||
												Expression.Constant(Activator.CreateInstance(typeof(List<>).MakeGenericType(refMiddleEntityType3)))
 | 
			
		||||
											));
 | 
			
		||||
										var manyMainParam = _tables[0].Parameter;
 | 
			
		||||
										var manyMainParam = tsc._tables[0].Parameter;
 | 
			
		||||
										var manySubSelectWhereParam = Expression.Parameter(parm123Ref.RefMiddleEntityType, $"M{fsqlWhereParam.Name}_M{asSelectParentExp.ToString().Replace(".", "__")}");//, $"{fsqlWhereParam.Name}__");
 | 
			
		||||
										Expression manySubSelectWhereExp = null;
 | 
			
		||||
										for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) {
 | 
			
		||||
@@ -528,7 +587,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
										manySubSelectExpBoy = Expression.Call(manySubSelectExpBoy, manySubSelectAny);
 | 
			
		||||
										asSelectBefores.Clear();
 | 
			
		||||
 | 
			
		||||
										return ExpressionLambdaToSql(manySubSelectExpBoy, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
										return ExpressionLambdaToSql(manySubSelectExpBoy, tsc);
 | 
			
		||||
									}
 | 
			
		||||
									for (var mn = 0; mn < parm123Ref.Columns.Count; mn++) {
 | 
			
		||||
										var col1 = parm123Ref.RefColumns[mn];
 | 
			
		||||
@@ -559,23 +618,23 @@ namespace FreeSql.Internal {
 | 
			
		||||
							
 | 
			
		||||
					//	}
 | 
			
		||||
					//}
 | 
			
		||||
					var other3Exp = ExpressionLambdaToSqlOther(exp3, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
					var other3Exp = ExpressionLambdaToSqlOther(exp3, tsc);
 | 
			
		||||
					if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp;
 | 
			
		||||
					throw new Exception($"未实现函数表达式 {exp3} 解析");
 | 
			
		||||
				case ExpressionType.Parameter:
 | 
			
		||||
				case ExpressionType.MemberAccess:
 | 
			
		||||
					var exp4 = exp as MemberExpression;
 | 
			
		||||
					if (exp4 != null) {
 | 
			
		||||
						if (exp4.Expression != null && exp4.Expression.Type.IsArray == false && exp4.Expression.Type.IsNullableType()) return ExpressionLambdaToSql(exp4.Expression, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						if (exp4.Expression != null && exp4.Expression.Type.IsArray == false && exp4.Expression.Type.IsNullableType()) return ExpressionLambdaToSql(exp4.Expression, tsc);
 | 
			
		||||
						var extRet = "";
 | 
			
		||||
						var memberType = exp4.Expression?.Type ?? exp4.Type;
 | 
			
		||||
						switch (memberType.FullName) {
 | 
			
		||||
							case "System.String": extRet = ExpressionLambdaToSqlMemberAccessString(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style); break;
 | 
			
		||||
							case "System.DateTime": extRet = ExpressionLambdaToSqlMemberAccessDateTime(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style); break;
 | 
			
		||||
							case "System.TimeSpan": extRet = ExpressionLambdaToSqlMemberAccessTimeSpan(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style); break;
 | 
			
		||||
							case "System.String": extRet = ExpressionLambdaToSqlMemberAccessString(exp4, tsc); break;
 | 
			
		||||
							case "System.DateTime": extRet = ExpressionLambdaToSqlMemberAccessDateTime(exp4, tsc); break;
 | 
			
		||||
							case "System.TimeSpan": extRet = ExpressionLambdaToSqlMemberAccessTimeSpan(exp4, tsc); break;
 | 
			
		||||
						}
 | 
			
		||||
						if (string.IsNullOrEmpty(extRet) == false) return extRet;
 | 
			
		||||
						var other4Exp = ExpressionLambdaToSqlOther(exp4, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
						var other4Exp = ExpressionLambdaToSqlOther(exp4, tsc);
 | 
			
		||||
						if (string.IsNullOrEmpty(other4Exp) == false) return other4Exp;
 | 
			
		||||
					}
 | 
			
		||||
					var expStack = new Stack<Expression>();
 | 
			
		||||
@@ -613,67 +672,67 @@ namespace FreeSql.Internal {
 | 
			
		||||
						}
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					if (expStack.First().NodeType != ExpressionType.Parameter) return _common.FormatSql("{0}", Expression.Lambda(exp).Compile().DynamicInvoke());
 | 
			
		||||
					if (callExp != null) return ExpressionLambdaToSql(callExp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
					if (getSelectGroupingMapString != null && expStack.First().Type.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`")) {
 | 
			
		||||
						if (getSelectGroupingMapString != null) {
 | 
			
		||||
							var expText = getSelectGroupingMapString(expStack.Where((a, b) => b >= 2).ToArray());
 | 
			
		||||
					if (expStack.First().NodeType != ExpressionType.Parameter) return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType);
 | 
			
		||||
					if (callExp != null) return ExpressionLambdaToSql(callExp, tsc);
 | 
			
		||||
					if (tsc.getSelectGroupingMapString != null && expStack.First().Type.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`")) {
 | 
			
		||||
						if (tsc.getSelectGroupingMapString != null) {
 | 
			
		||||
							var expText = tsc.getSelectGroupingMapString(expStack.Where((a, b) => b >= 2).ToArray());
 | 
			
		||||
							if (string.IsNullOrEmpty(expText) == false) return expText;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (_tables == null) {
 | 
			
		||||
					if (tsc._tables == null) {
 | 
			
		||||
						var pp = expStack.Pop() as ParameterExpression;
 | 
			
		||||
						var memberExp = expStack.Pop() as MemberExpression;
 | 
			
		||||
						var tb = _common.GetTableByEntity(pp.Type);
 | 
			
		||||
						if (tb.ColumnsByCs.ContainsKey(memberExp.Member.Name) == false) throw new ArgumentException($"{tb.DbName} 找不到列 {memberExp.Member.Name}");
 | 
			
		||||
						if (_selectColumnMap != null) {
 | 
			
		||||
							_selectColumnMap.Add(new SelectColumnInfo { Table = null, Column = tb.ColumnsByCs[memberExp.Member.Name] });
 | 
			
		||||
						if (tsc._selectColumnMap != null) {
 | 
			
		||||
							tsc._selectColumnMap.Add(new SelectColumnInfo { Table = null, Column = tb.ColumnsByCs[memberExp.Member.Name] });
 | 
			
		||||
						}
 | 
			
		||||
						var name = tb.ColumnsByCs[memberExp.Member.Name].Attribute.Name;
 | 
			
		||||
						if (isQuoteName) name = _common.QuoteSqlName(name);
 | 
			
		||||
						if (tsc.isQuoteName) name = _common.QuoteSqlName(name);
 | 
			
		||||
						return name;
 | 
			
		||||
					}
 | 
			
		||||
					Func<TableInfo, string, bool, ParameterExpression, MemberExpression, SelectTableInfo> getOrAddTable = (tbtmp, alias, isa, parmExp, mp) => {
 | 
			
		||||
						var finds = new SelectTableInfo[0];
 | 
			
		||||
						if (style == ExpressionStyle.SelectColumns) {
 | 
			
		||||
							finds = _tables.Where(a => a.Table.Type == tbtmp.Type).ToArray();
 | 
			
		||||
						if (tsc.style == ExpressionStyle.SelectColumns) {
 | 
			
		||||
							finds = tsc._tables.Where(a => a.Table.Type == tbtmp.Type).ToArray();
 | 
			
		||||
							if (finds.Any()) finds = new[] { finds.First() };
 | 
			
		||||
						}
 | 
			
		||||
						if (finds.Length != 1 && isa && parmExp != null)
 | 
			
		||||
							finds = _tables.Where(a => a.Parameter == parmExp).ToArray();
 | 
			
		||||
							finds = tsc._tables.Where(a => a.Parameter == parmExp).ToArray();
 | 
			
		||||
						if (finds.Length != 1) {
 | 
			
		||||
							var navdot = string.IsNullOrEmpty(alias) ? new SelectTableInfo[0] : _tables.Where(a2 => a2.Parameter != null && alias.StartsWith($"{a2.Alias}__")).ToArray();
 | 
			
		||||
							var navdot = string.IsNullOrEmpty(alias) ? new SelectTableInfo[0] : tsc._tables.Where(a2 => a2.Parameter != null && alias.StartsWith($"{a2.Alias}__")).ToArray();
 | 
			
		||||
							if (navdot.Length > 0) {
 | 
			
		||||
								var isthis = navdot[0] == _tables[0];
 | 
			
		||||
								finds = _tables.Where(a2 => (isa && a2.Parameter != null || !isa && a2.Parameter == null) &&
 | 
			
		||||
								var isthis = navdot[0] == tsc._tables[0];
 | 
			
		||||
								finds = tsc._tables.Where(a2 => (isa && a2.Parameter != null || !isa && a2.Parameter == null) &&
 | 
			
		||||
									a2.Table.Type == tbtmp.Type && a2.Alias == alias && a2.Alias.StartsWith($"{navdot[0].Alias}__") &&
 | 
			
		||||
									(isthis && a2.Type != SelectTableInfoType.Parent || !isthis && a2.Type == SelectTableInfoType.Parent)).ToArray();
 | 
			
		||||
								if (finds.Length == 0)
 | 
			
		||||
									finds = _tables.Where(a2 => 
 | 
			
		||||
									finds = tsc._tables.Where(a2 => 
 | 
			
		||||
										 a2.Table.Type == tbtmp.Type && a2.Alias == alias && a2.Alias.StartsWith($"{navdot[0].Alias}__") &&
 | 
			
		||||
										 (isthis && a2.Type != SelectTableInfoType.Parent || !isthis && a2.Type == SelectTableInfoType.Parent)).ToArray();
 | 
			
		||||
							} else {
 | 
			
		||||
								finds = _tables.Where(a2 => (isa && a2.Parameter != null || isa && a2.Parameter == null) &&
 | 
			
		||||
								finds = tsc._tables.Where(a2 => (isa && a2.Parameter != null || isa && a2.Parameter == null) &&
 | 
			
		||||
									a2.Table.Type == tbtmp.Type && a2.Alias == alias).ToArray();
 | 
			
		||||
								if (finds.Length != 1) {
 | 
			
		||||
									finds = _tables.Where(a2 => (isa && a2.Parameter != null || isa && a2.Parameter == null) &&
 | 
			
		||||
									finds = tsc._tables.Where(a2 => (isa && a2.Parameter != null || isa && a2.Parameter == null) &&
 | 
			
		||||
										a2.Table.Type == tbtmp.Type).ToArray();
 | 
			
		||||
									if (finds.Length != 1) {
 | 
			
		||||
										finds = _tables.Where(a2 => (isa && a2.Parameter != null || isa && a2.Parameter == null) &&
 | 
			
		||||
										finds = tsc._tables.Where(a2 => (isa && a2.Parameter != null || isa && a2.Parameter == null) &&
 | 
			
		||||
											a2.Table.Type == tbtmp.Type).ToArray();
 | 
			
		||||
										if (finds.Length != 1)
 | 
			
		||||
											finds = _tables.Where(a2 => a2.Table.Type == tbtmp.Type).ToArray();
 | 
			
		||||
											finds = tsc._tables.Where(a2 => a2.Table.Type == tbtmp.Type).ToArray();
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
							//finds = _tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && (isthis && a2.Type != SelectTableInfoType.Parent || !isthis)).ToArray(); //外部表,内部表一起查
 | 
			
		||||
							//finds = tsc._tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && (isthis && a2.Type != SelectTableInfoType.Parent || !isthis)).ToArray(); //外部表,内部表一起查
 | 
			
		||||
							//if (finds.Length > 1) {
 | 
			
		||||
							//	finds = _tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && a2.Type == SelectTableInfoType.Parent && a2.Alias == alias).ToArray(); //查询外部表
 | 
			
		||||
							//	finds = tsc._tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && a2.Type == SelectTableInfoType.Parent && a2.Alias == alias).ToArray(); //查询外部表
 | 
			
		||||
							//	if (finds.Any() == false) {
 | 
			
		||||
							//		finds = _tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && a2.Type != SelectTableInfoType.Parent).ToArray(); //查询内部表
 | 
			
		||||
							//		finds = tsc._tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && a2.Type != SelectTableInfoType.Parent).ToArray(); //查询内部表
 | 
			
		||||
							//		if (finds.Length > 1)
 | 
			
		||||
							//			finds = _tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && a2.Type != SelectTableInfoType.Parent && a2.Alias == alias).ToArray();
 | 
			
		||||
							//			finds = tsc._tables.Where((a2, c2) => (isa || a2.Parameter == null) && a2.Table.CsName == tbtmp.CsName && a2.Type != SelectTableInfoType.Parent && a2.Alias == alias).ToArray();
 | 
			
		||||
							//	}
 | 
			
		||||
							//}
 | 
			
		||||
						}
 | 
			
		||||
@@ -681,11 +740,11 @@ namespace FreeSql.Internal {
 | 
			
		||||
						if (find != null && isa && parmExp != null && find.Parameter != parmExp)
 | 
			
		||||
							find.Parameter = parmExp;
 | 
			
		||||
						if (find == null) {
 | 
			
		||||
							_tables.Add(find = new SelectTableInfo { Table = tbtmp, Alias = alias, On = null, Type = mp == null ? tbtype : SelectTableInfoType.LeftJoin, Parameter = isa ? parmExp : null });
 | 
			
		||||
							tsc._tables.Add(find = new SelectTableInfo { Table = tbtmp, Alias = alias, On = null, Type = mp == null ? tsc.tbtype : SelectTableInfoType.LeftJoin, Parameter = isa ? parmExp : null });
 | 
			
		||||
							if (mp?.Expression != null) { //导航条件,OneToOne、ManyToOne
 | 
			
		||||
								var firstTb = _tables.First().Table;
 | 
			
		||||
								var firstTb = tsc._tables.First().Table;
 | 
			
		||||
								var parentTb = _common.GetTableByEntity(mp.Expression.Type);
 | 
			
		||||
								var parentTbRef = parentTb.GetTableRef(mp.Member.Name, style == ExpressionStyle.AsSelect);
 | 
			
		||||
								var parentTbRef = parentTb.GetTableRef(mp.Member.Name, tsc.style == ExpressionStyle.AsSelect);
 | 
			
		||||
								if (parentTbRef != null) {
 | 
			
		||||
									Expression navCondExp = null;
 | 
			
		||||
									for (var mn = 0; mn < parentTbRef.Columns.Count; mn++) {
 | 
			
		||||
@@ -704,9 +763,9 @@ namespace FreeSql.Internal {
 | 
			
		||||
									if (find.Type == SelectTableInfoType.InnerJoin ||
 | 
			
		||||
										find.Type == SelectTableInfoType.LeftJoin ||
 | 
			
		||||
										find.Type == SelectTableInfoType.RightJoin)
 | 
			
		||||
										find.On = ExpressionLambdaToSql(navCondExp, _tables, null, null, find.Type, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
										find.On = ExpressionLambdaToSql(navCondExp, tsc.CloneSetgetSelectGroupingMapStringAndgetSelectGroupingMapStringAndtbtype(null, null, find.Type));
 | 
			
		||||
									else
 | 
			
		||||
										find.NavigateCondition = ExpressionLambdaToSql(navCondExp, _tables, null, null, find.Type, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
										find.NavigateCondition = ExpressionLambdaToSql(navCondExp, tsc.CloneSetgetSelectGroupingMapStringAndgetSelectGroupingMapStringAndtbtype(null, null, find.Type));
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
@@ -740,29 +799,29 @@ namespace FreeSql.Internal {
 | 
			
		||||
									tb2 = tb2tmp;
 | 
			
		||||
								}
 | 
			
		||||
								if (exp2.NodeType == ExpressionType.Parameter && expStack.Any() == false) { //附加选择的参数所有列
 | 
			
		||||
									if (_selectColumnMap != null) {
 | 
			
		||||
									if (tsc._selectColumnMap != null) {
 | 
			
		||||
										foreach (var tb2c in tb2.Columns.Values)
 | 
			
		||||
											_selectColumnMap.Add(new SelectColumnInfo { Table = find2, Column = tb2c });
 | 
			
		||||
											tsc._selectColumnMap.Add(new SelectColumnInfo { Table = find2, Column = tb2c });
 | 
			
		||||
										if (tb2.Columns.Any()) return "";
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
								if (mp2 == null || expStack.Any()) continue;
 | 
			
		||||
								if (tb2.ColumnsByCs.ContainsKey(mp2.Member.Name) == false) { //如果选的是对象,附加所有列
 | 
			
		||||
									if (_selectColumnMap != null) {
 | 
			
		||||
									if (tsc._selectColumnMap != null) {
 | 
			
		||||
										var tb3 = _common.GetTableByEntity(mp2.Type);
 | 
			
		||||
										if (tb3 != null) {
 | 
			
		||||
											var find3 = getOrAddTable(tb2tmp, alias2 /*$"{alias2}__{mp2.Member.Name}"*/, exp2.NodeType == ExpressionType.Parameter, parmExp2, mp2);
 | 
			
		||||
 | 
			
		||||
											foreach (var tb3c in tb3.Columns.Values)
 | 
			
		||||
												_selectColumnMap.Add(new SelectColumnInfo { Table = find3, Column = tb3c });
 | 
			
		||||
												tsc._selectColumnMap.Add(new SelectColumnInfo { Table = find3, Column = tb3c });
 | 
			
		||||
											if (tb3.Columns.Any()) return "";
 | 
			
		||||
										}
 | 
			
		||||
									}
 | 
			
		||||
									throw new ArgumentException($"{tb2.DbName} 找不到列 {mp2.Member.Name}");
 | 
			
		||||
								}
 | 
			
		||||
								var col2 = tb2.ColumnsByCs[mp2.Member.Name];
 | 
			
		||||
								if (_selectColumnMap != null && find2 != null) {
 | 
			
		||||
									_selectColumnMap.Add(new SelectColumnInfo { Table = find2, Column = col2 });
 | 
			
		||||
								if (tsc._selectColumnMap != null && find2 != null) {
 | 
			
		||||
									tsc._selectColumnMap.Add(new SelectColumnInfo { Table = find2, Column = col2 });
 | 
			
		||||
									return "";
 | 
			
		||||
								}
 | 
			
		||||
								name2 = tb2.ColumnsByCs[mp2.Member.Name].Attribute.Name;
 | 
			
		||||
@@ -770,63 +829,78 @@ namespace FreeSql.Internal {
 | 
			
		||||
							case ExpressionType.Call:break;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if (isQuoteName) name2 = _common.QuoteSqlName(name2);
 | 
			
		||||
					if (tsc.isQuoteName) name2 = _common.QuoteSqlName(name2);
 | 
			
		||||
					return $"{alias2}.{name2}";
 | 
			
		||||
			}
 | 
			
		||||
			var expBinary = exp as BinaryExpression;
 | 
			
		||||
			if (expBinary == null) {
 | 
			
		||||
				var other99Exp = ExpressionLambdaToSqlOther(exp, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
				var other99Exp = ExpressionLambdaToSqlOther(exp, tsc);
 | 
			
		||||
				if (string.IsNullOrEmpty(other99Exp) == false) return other99Exp;
 | 
			
		||||
				return "";
 | 
			
		||||
			}
 | 
			
		||||
			switch (expBinary.NodeType) {
 | 
			
		||||
				case ExpressionType.Coalesce:
 | 
			
		||||
					return _common.IsNull(ExpressionLambdaToSql(expBinary.Left, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style), ExpressionLambdaToSql(expBinary.Right, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style));
 | 
			
		||||
					return _common.IsNull(ExpressionLambdaToSql(expBinary.Left, tsc), ExpressionLambdaToSql(expBinary.Right, tsc));
 | 
			
		||||
				case ExpressionType.OrElse:
 | 
			
		||||
					return $"({ExpressionLambdaToSql(expBinary.Left, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)} OR {ExpressionLambdaToSql(expBinary.Right, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style)})";
 | 
			
		||||
					return $"({ExpressionLambdaToSql(expBinary.Left, tsc)} OR {ExpressionLambdaToSql(expBinary.Right, tsc)})";
 | 
			
		||||
			}
 | 
			
		||||
			if (dicExpressionOperator.TryGetValue(expBinary.NodeType, out var tryoper) == false) return "";
 | 
			
		||||
			var left = ExpressionLambdaToSql(expBinary.Left, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
			var right = ExpressionLambdaToSql(expBinary.Right, _tables, _selectColumnMap, getSelectGroupingMapString, tbtype, isQuoteName, isDisableDiyParse, style);
 | 
			
		||||
			if (left == "NULL") {
 | 
			
		||||
				var tmp = right;
 | 
			
		||||
				right = left;
 | 
			
		||||
				left = tmp;
 | 
			
		||||
			}
 | 
			
		||||
			if (right == "NULL") tryoper = tryoper == "=" ? " IS " : " IS NOT ";
 | 
			
		||||
			if (tryoper == "+" && (expBinary.Left.Type.FullName == "System.String" || expBinary.Right.Type.FullName == "System.String")) return _common.StringConcat(new[] { left, right }, new[] { expBinary.Left.Type, expBinary.Right.Type });
 | 
			
		||||
			if (tryoper == "%") return _common.Mod(left, right, expBinary.Left.Type, expBinary.Right.Type);
 | 
			
		||||
			if (_common._orm.Ado.DataType == DataType.MySql) {
 | 
			
		||||
				//处理c#变态enum convert, a.EnumType1 == Xxx.Xxx,被转成了 Convert(a.EnumType1, Int32) == 1
 | 
			
		||||
				if (expBinary.Left.NodeType == ExpressionType.Convert && expBinary.Right.NodeType == ExpressionType.Constant) {
 | 
			
		||||
					if (long.TryParse(right, out var tryenumLong)) {
 | 
			
		||||
						var enumType = (expBinary.Left as UnaryExpression)?.Operand.Type;
 | 
			
		||||
						if (enumType?.IsEnum == true)
 | 
			
		||||
							right = _common.FormatSql("{0}", Enum.Parse(enumType, right));
 | 
			
		||||
					}
 | 
			
		||||
				} else if (expBinary.Left.NodeType == ExpressionType.Constant && expBinary.Right.NodeType == ExpressionType.Convert) {
 | 
			
		||||
					if (long.TryParse(left, out var tryenumLong)) {
 | 
			
		||||
						var enumType = (expBinary.Right as UnaryExpression)?.Operand.Type;
 | 
			
		||||
						if (enumType?.IsEnum == true)
 | 
			
		||||
							left = _common.FormatSql("{0}", Enum.Parse(enumType, left));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return $"{left} {tryoper} {right}";
 | 
			
		||||
 | 
			
		||||
			return ExpressionBinary(tryoper, expBinary.Left, expBinary.Right, tsc);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallString(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlOther(Expression exp, List<SelectTableInfo> _tables, List<SelectColumnInfo> _selectColumnMap, Func<Expression[], string> getSelectGroupingMapString, SelectTableInfoType tbtype, bool isQuoteName, bool isDisableDiyParse, ExpressionStyle style);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc);
 | 
			
		||||
		internal abstract string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc);
 | 
			
		||||
 | 
			
		||||
		internal enum ExpressionStyle {
 | 
			
		||||
			Where, AsSelect, SelectColumns
 | 
			
		||||
		}
 | 
			
		||||
		internal class ExpTSC {
 | 
			
		||||
			public List<SelectTableInfo> _tables { get; set; }
 | 
			
		||||
			public List<SelectColumnInfo> _selectColumnMap { get; set; }
 | 
			
		||||
			public Func<Expression[], string> getSelectGroupingMapString { get; set; }
 | 
			
		||||
			public SelectTableInfoType tbtype { get; set; }
 | 
			
		||||
			public bool isQuoteName { get; set; }
 | 
			
		||||
			public bool isDisableDiyParse { get; set; }
 | 
			
		||||
			public ExpressionStyle style { get; set; }
 | 
			
		||||
			public Type mapType { get; set; }
 | 
			
		||||
			public TableInfo currentTable { get; set; }
 | 
			
		||||
 | 
			
		||||
			public ExpTSC CloneSetgetSelectGroupingMapStringAndgetSelectGroupingMapStringAndtbtype(List<SelectColumnInfo> v1, Func<Expression[], string> v2, SelectTableInfoType v3 ) {
 | 
			
		||||
				return new ExpTSC {
 | 
			
		||||
					_tables = this._tables,
 | 
			
		||||
					_selectColumnMap = v1,
 | 
			
		||||
					getSelectGroupingMapString = v2,
 | 
			
		||||
					tbtype = v3,
 | 
			
		||||
					isQuoteName = this.isQuoteName,
 | 
			
		||||
					isDisableDiyParse = this.isDisableDiyParse,
 | 
			
		||||
					style = this.style,
 | 
			
		||||
					currentTable = this.currentTable
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
			public ExpTSC CloneDisableDiyParse() {
 | 
			
		||||
				return new ExpTSC {
 | 
			
		||||
					_tables = this._tables,
 | 
			
		||||
					_selectColumnMap = this._selectColumnMap,
 | 
			
		||||
					getSelectGroupingMapString = this.getSelectGroupingMapString,
 | 
			
		||||
					tbtype = this.tbtype,
 | 
			
		||||
					isQuoteName = this.isQuoteName,
 | 
			
		||||
					isDisableDiyParse = false,
 | 
			
		||||
					style = this.style,
 | 
			
		||||
					currentTable = this.currentTable
 | 
			
		||||
				};
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal string formatSql(object obj, Type mapType) {
 | 
			
		||||
			return string.Concat(_ado.AddslashesProcessParam(obj, mapType));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
	partial class AdoProvider {
 | 
			
		||||
 | 
			
		||||
		public abstract object AddslashesProcessParam(object param);
 | 
			
		||||
		public abstract object AddslashesProcessParam(object param, Type mapType);
 | 
			
		||||
		public string Addslashes(string filter, params object[] parms) {
 | 
			
		||||
			if (filter == null || parms == null) return string.Empty;
 | 
			
		||||
			if (parms.Length == 0) return filter;
 | 
			
		||||
@@ -11,7 +11,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
			for (int a = 0; a < parms.Length; a++) {
 | 
			
		||||
				if (parms[a] == null)
 | 
			
		||||
					filter = Regex.Replace(filter, @"\s*(=|IN)\s*\{" + a + @"\}", " IS {" + a + "}", RegexOptions.IgnoreCase);
 | 
			
		||||
				nparms[a] = AddslashesProcessParam(parms[a]);
 | 
			
		||||
				nparms[a] = AddslashesProcessParam(parms[a], null);
 | 
			
		||||
			}
 | 
			
		||||
			try { string ret = string.Format(filter, nparms); return ret; } catch { return filter; }
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
		public abstract List<T1> ExecuteDeleted();
 | 
			
		||||
		public abstract Task<List<T1>> ExecuteDeletedAsync();
 | 
			
		||||
 | 
			
		||||
		public IDelete<T1> Where(Expression<Func<T1, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambdaNoneForeignObject(null, null, exp?.Body, null));
 | 
			
		||||
		public IDelete<T1> Where(Expression<Func<T1, bool>> exp) => this.Where(_commonExpression.ExpressionWhereLambdaNoneForeignObject(null, _table, null, exp?.Body, null));
 | 
			
		||||
		public IDelete<T1> Where(string sql, object parms = null) {
 | 
			
		||||
			if (string.IsNullOrEmpty(sql)) return this;
 | 
			
		||||
			var args = new AopWhereEventArgs(sql, parms);
 | 
			
		||||
 
 | 
			
		||||
@@ -387,17 +387,14 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
				foreach (var col in _table.Columns.Values)
 | 
			
		||||
					if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.Attribute.Name) == false) {
 | 
			
		||||
						if (colidx2 > 0) sb.Append(", ");
 | 
			
		||||
						object val = null;
 | 
			
		||||
						if (_table.Properties.TryGetValue(col.CsName, out var tryp)) {
 | 
			
		||||
							val = tryp.GetValue(d);
 | 
			
		||||
							if (col.Attribute.IsPrimary && (col.CsType == typeof(Guid) || col.CsType == typeof(Guid?))
 | 
			
		||||
								&& (val == null || (Guid)val == Guid.Empty)) tryp.SetValue(d, val = FreeUtil.NewMongodbId());
 | 
			
		||||
						}
 | 
			
		||||
						object val = col.GetMapValue(d);
 | 
			
		||||
						if (col.Attribute.IsPrimary && col.Attribute.MapType.NullableTypeOrThis() == typeof(Guid) && (val == null || (Guid)val == Guid.Empty))
 | 
			
		||||
							col.SetMapValue(d, val = FreeUtil.NewMongodbId());
 | 
			
		||||
						if (_noneParameter)
 | 
			
		||||
							sb.Append(_commonUtils.GetNoneParamaterSqlValue(specialParams, col.CsType, val));
 | 
			
		||||
							sb.Append(_commonUtils.GetNoneParamaterSqlValue(specialParams, col.Attribute.MapType, val));
 | 
			
		||||
						else {
 | 
			
		||||
							sb.Append(_commonUtils.QuoteWriteParamter(col.CsType, _commonUtils.QuoteParamterName($"{col.CsName}_{didx}")));
 | 
			
		||||
							_params[didx * colidx + colidx2] = _commonUtils.AppendParamter(null, $"{col.CsName}_{didx}", col.CsType, val);
 | 
			
		||||
							sb.Append(_commonUtils.QuoteWriteParamter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"{col.CsName}_{didx}")));
 | 
			
		||||
							_params[didx * colidx + colidx2] = _commonUtils.AppendParamter(null, $"{col.CsName}_{didx}", col.Attribute.MapType, val);
 | 
			
		||||
						}
 | 
			
		||||
						++colidx2;
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
@@ -453,7 +453,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
							if (tbiindex > 0 && colidx == 0) field.Append("\r\n");
 | 
			
		||||
						}
 | 
			
		||||
						var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name);
 | 
			
		||||
						field.Append(_commonUtils.QuoteReadColumn(col.CsType, $"{tbi.Alias}.{quoteName}"));
 | 
			
		||||
						field.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"{tbi.Alias}.{quoteName}"));
 | 
			
		||||
						++index;
 | 
			
		||||
						if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
 | 
			
		||||
						else dicfield.Add(quoteName, true);
 | 
			
		||||
@@ -505,7 +505,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
					if (tb.Table.ColumnsByCs.TryGetValue(prop.Name, out var col)) { //普通字段
 | 
			
		||||
						if (index > 0) field.Append(", ");
 | 
			
		||||
						var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name);
 | 
			
		||||
						field.Append(_commonUtils.QuoteReadColumn(col.CsType, $"{tb.Alias}.{quoteName}"));
 | 
			
		||||
						field.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"{tb.Alias}.{quoteName}"));
 | 
			
		||||
						++index;
 | 
			
		||||
						if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
 | 
			
		||||
						else dicfield.Add(quoteName, true);
 | 
			
		||||
@@ -525,7 +525,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
						foreach (var col2 in tb2.Table.Columns.Values) {
 | 
			
		||||
							if (index > 0) field.Append(", ");
 | 
			
		||||
							var quoteName = _commonUtils.QuoteSqlName(col2.Attribute.Name);
 | 
			
		||||
							field.Append(_commonUtils.QuoteReadColumn(col2.CsType, $"{tb2.Alias}.{quoteName}"));
 | 
			
		||||
							field.Append(_commonUtils.QuoteReadColumn(col2.Attribute.MapType, $"{tb2.Alias}.{quoteName}"));
 | 
			
		||||
							++index;
 | 
			
		||||
							++otherindex;
 | 
			
		||||
							if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
 | 
			
		||||
@@ -607,7 +607,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
				if (tb.Table.ColumnsByCs.TryGetValue(p.Name, out var col)) { //普通字段
 | 
			
		||||
					if (index > 0) field.Append(", ");
 | 
			
		||||
					var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name);
 | 
			
		||||
					field.Append(_commonUtils.QuoteReadColumn(col.CsType, $"{tb.Alias}.{quoteName}"));
 | 
			
		||||
					field.Append(_commonUtils.QuoteReadColumn(col.Attribute.MapType, $"{tb.Alias}.{quoteName}"));
 | 
			
		||||
					++index;
 | 
			
		||||
					if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
 | 
			
		||||
					else dicfield.Add(quoteName, true);
 | 
			
		||||
@@ -620,7 +620,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
					foreach (var col2 in tb2.Table.Columns.Values) {
 | 
			
		||||
						if (index > 0) field.Append(", ");
 | 
			
		||||
						var quoteName = _commonUtils.QuoteSqlName(col2.Attribute.Name);
 | 
			
		||||
						field.Append(_commonUtils.QuoteReadColumn(col2.CsType, $"{tb2.Alias}.{quoteName}"));
 | 
			
		||||
						field.Append(_commonUtils.QuoteReadColumn(col2.Attribute.MapType, $"{tb2.Alias}.{quoteName}"));
 | 
			
		||||
						++index;
 | 
			
		||||
						if (dicfield.ContainsKey(quoteName)) field.Append(" as").Append(index);
 | 
			
		||||
						else dicfield.Add(quoteName, true);
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
		string getSelectGroupingMapString(Expression[] members) {
 | 
			
		||||
			if (members.Any() == false) return _map.DbField;
 | 
			
		||||
			var parentName = ((members.FirstOrDefault() as MemberExpression)?.Expression as MemberExpression)?.Member.Name;
 | 
			
		||||
			switch(parentName) {
 | 
			
		||||
			switch (parentName) {
 | 
			
		||||
				case "Key":
 | 
			
		||||
					var read = _map;
 | 
			
		||||
					for (var a = 0; a < members.Length; a++) {
 | 
			
		||||
@@ -53,7 +53,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
					var parmExp = Expression.Parameter(tb.Table.Type, tb.Alias);
 | 
			
		||||
					Expression retExp = parmExp;
 | 
			
		||||
					for (var a = foridx; a < members.Length; a++) {
 | 
			
		||||
						switch(members[a].NodeType) {
 | 
			
		||||
						switch (members[a].NodeType) {
 | 
			
		||||
							case ExpressionType.Call:
 | 
			
		||||
								retExp = Expression.Call(retExp, (members[a] as MethodCallExpression).Method);
 | 
			
		||||
								break;
 | 
			
		||||
@@ -64,7 +64,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
								return null;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					return _comonExp.ExpressionLambdaToSql(retExp, _tables, null, null, SelectTableInfoType.From, true, true, CommonExpression.ExpressionStyle.Where);
 | 
			
		||||
					return _comonExp.ExpressionLambdaToSql(retExp, new CommonExpression.ExpTSC { _tables = _tables, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = true, style = CommonExpression.ExpressionStyle.Where });
 | 
			
		||||
			}
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -301,10 +301,10 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
			var col = cols.First();
 | 
			
		||||
			_set.Append(", ").Append(_commonUtils.QuoteSqlName(col.Column.Attribute.Name)).Append(" = ");
 | 
			
		||||
			if (_noneParameter) {
 | 
			
		||||
				_set.Append(_commonUtils.GetNoneParamaterSqlValue(_params, col.Column.CsType, value));
 | 
			
		||||
				_set.Append(_commonUtils.GetNoneParamaterSqlValue(_params, col.Column.Attribute.MapType, value));
 | 
			
		||||
			} else {
 | 
			
		||||
				_set.Append(_commonUtils.QuoteWriteParamter(col.Column.CsType, $"{_commonUtils.QuoteParamterName("p_")}{_params.Count}"));
 | 
			
		||||
				_commonUtils.AppendParamter(_params, null, col.Column.CsType, value);
 | 
			
		||||
				_set.Append(_commonUtils.QuoteWriteParamter(col.Column.Attribute.MapType, $"{_commonUtils.QuoteParamterName("p_")}{_params.Count}"));
 | 
			
		||||
				_commonUtils.AppendParamter(_params, null, col.Column.Attribute.MapType, value);
 | 
			
		||||
			}
 | 
			
		||||
			//foreach (var t in _source) Utils.FillPropertyValue(t, tryf.CsName, value);
 | 
			
		||||
			return this;
 | 
			
		||||
@@ -313,11 +313,11 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
			if (binaryExpression?.Body is BinaryExpression == false &&
 | 
			
		||||
				binaryExpression?.Body.NodeType != ExpressionType.Call) return this;
 | 
			
		||||
			var cols = new List<SelectColumnInfo>();
 | 
			
		||||
			var expt = _commonExpression.ExpressionWhereLambdaNoneForeignObject(null, cols, binaryExpression, null);
 | 
			
		||||
			var expt = _commonExpression.ExpressionWhereLambdaNoneForeignObject(null, _table, cols, binaryExpression, null);
 | 
			
		||||
			if (cols.Any() == false) return this;
 | 
			
		||||
			foreach (var col in cols) {
 | 
			
		||||
				if (col.Column.Attribute.IsNullable == true && col.Column.CsType.IsNullableType()) {
 | 
			
		||||
					var replval = _orm.CodeFirst.GetDbInfo(col.Column.CsType.GenericTypeArguments.FirstOrDefault())?.defaultValue;
 | 
			
		||||
				if (col.Column.Attribute.IsNullable == true && col.Column.Attribute.MapType.IsNullableType()) {
 | 
			
		||||
					var replval = _orm.CodeFirst.GetDbInfo(col.Column.Attribute.MapType.GenericTypeArguments.FirstOrDefault())?.defaultValue;
 | 
			
		||||
					if (replval == null) continue;
 | 
			
		||||
					var replname = _commonUtils.QuoteSqlName(col.Column.Attribute.Name);
 | 
			
		||||
					expt = expt.Replace(replname, _commonUtils.IsNull(replname, _commonUtils.FormatSql("{0}", replval)));
 | 
			
		||||
@@ -333,7 +333,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
			return this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IUpdate<T1> Where(Expression<Func<T1, bool>> expression) => this.Where(_commonExpression.ExpressionWhereLambdaNoneForeignObject(null, null, expression?.Body, null));
 | 
			
		||||
		public IUpdate<T1> Where(Expression<Func<T1, bool>> expression) => this.Where(_commonExpression.ExpressionWhereLambdaNoneForeignObject(null, _table, null, expression?.Body, null));
 | 
			
		||||
		public IUpdate<T1> Where(string sql, object parms = null) {
 | 
			
		||||
			if (string.IsNullOrEmpty(sql)) return this;
 | 
			
		||||
			var args = new AopWhereEventArgs(sql, parms);
 | 
			
		||||
@@ -361,8 +361,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
				var sb = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
				sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ");
 | 
			
		||||
				var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(_source.First()) : DBNull.Value;
 | 
			
		||||
				sb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value)));
 | 
			
		||||
				sb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.Attribute.MapType, col.GetMapValue(_source.First()))));
 | 
			
		||||
 | 
			
		||||
				return sb.ToString();
 | 
			
		||||
 | 
			
		||||
@@ -382,8 +381,8 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
					cwsb.Append(" \r\nWHEN ");
 | 
			
		||||
					ToSqlWhen(cwsb, _table.Primarys, d);
 | 
			
		||||
					cwsb.Append(" THEN ");
 | 
			
		||||
					var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value;
 | 
			
		||||
					cwsb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value)));
 | 
			
		||||
					var value = col.GetMapValue(d);
 | 
			
		||||
					cwsb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.Attribute.MapType, value)));
 | 
			
		||||
					if (isnull == false) isnull = value == null || value == DBNull.Value;
 | 
			
		||||
				}
 | 
			
		||||
				cwsb.Append(" END");
 | 
			
		||||
@@ -427,12 +426,12 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
					if (col.Attribute.IsIdentity == false && col.Attribute.IsVersion == false && _ignore.ContainsKey(col.CsName) == false) {
 | 
			
		||||
						if (colidx > 0) sb.Append(", ");
 | 
			
		||||
						sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ");
 | 
			
		||||
						var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(_source.First()) : null;
 | 
			
		||||
						var value = col.GetMapValue(_source.First());
 | 
			
		||||
						if (_noneParameter) {
 | 
			
		||||
							sb.Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value));
 | 
			
		||||
							sb.Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.Attribute.MapType, value));
 | 
			
		||||
						} else {
 | 
			
		||||
							sb.Append(_commonUtils.QuoteWriteParamter(col.CsType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}")));
 | 
			
		||||
							_commonUtils.AppendParamter(_paramsSource, null, col.CsType, value);
 | 
			
		||||
							sb.Append(_commonUtils.QuoteWriteParamter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}")));
 | 
			
		||||
							_commonUtils.AppendParamter(_paramsSource, null, col.Attribute.MapType, value);
 | 
			
		||||
						}
 | 
			
		||||
						++colidx;
 | 
			
		||||
					}
 | 
			
		||||
@@ -460,12 +459,12 @@ namespace FreeSql.Internal.CommonProvider {
 | 
			
		||||
							cwsb.Append(" \r\nWHEN ");
 | 
			
		||||
							ToSqlWhen(cwsb, _table.Primarys, d);
 | 
			
		||||
							cwsb.Append(" THEN ");
 | 
			
		||||
							var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value;
 | 
			
		||||
							var value = col.GetMapValue(d);
 | 
			
		||||
							if (_noneParameter) {
 | 
			
		||||
								cwsb.Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value));
 | 
			
		||||
								cwsb.Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.Attribute.MapType, value));
 | 
			
		||||
							} else {
 | 
			
		||||
								cwsb.Append(_commonUtils.QuoteWriteParamter(col.CsType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}")));
 | 
			
		||||
								_commonUtils.AppendParamter(_paramsSource, null, col.CsType, value);
 | 
			
		||||
								cwsb.Append(_commonUtils.QuoteWriteParamter(col.Attribute.MapType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}")));
 | 
			
		||||
								_commonUtils.AppendParamter(_paramsSource, null, col.Attribute.MapType, value);
 | 
			
		||||
							}
 | 
			
		||||
							if (isnull == false) isnull = value == null || value == DBNull.Value;
 | 
			
		||||
						}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
		internal abstract DbParameter[] GetDbParamtersByObject(string sql, object obj);
 | 
			
		||||
		internal abstract string FormatSql(string sql, params object[] args);
 | 
			
		||||
		internal abstract string QuoteSqlName(string name);
 | 
			
		||||
		internal abstract string TrimQuoteSqlName(string name);
 | 
			
		||||
		internal abstract string QuoteParamterName(string name);
 | 
			
		||||
		internal abstract string IsNull(string sql, object value);
 | 
			
		||||
		internal abstract string StringConcat(string[] objs, Type[] types);
 | 
			
		||||
@@ -100,6 +101,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
				if (trycol._IsNullable != null) attr._IsNullable = trycol.IsNullable;
 | 
			
		||||
				if (trycol._IsIgnore != null) attr._IsIgnore = trycol.IsIgnore;
 | 
			
		||||
				if (trycol._IsVersion != null) attr._IsVersion = trycol.IsVersion;
 | 
			
		||||
				if (trycol.MapType != null) attr.MapType = trycol.MapType;
 | 
			
		||||
				if (trycol.DbDefautValue != null) attr.DbDefautValue = trycol.DbDefautValue;
 | 
			
		||||
			}
 | 
			
		||||
			var attrs = proto.GetCustomAttributes(typeof(ColumnAttribute), false);
 | 
			
		||||
@@ -114,34 +116,38 @@ namespace FreeSql.Internal {
 | 
			
		||||
				if (tryattr._IsNullable != null) attr._IsNullable = tryattr.IsNullable;
 | 
			
		||||
				if (tryattr._IsIgnore != null) attr._IsIgnore = tryattr.IsIgnore;
 | 
			
		||||
				if (tryattr._IsVersion != null) attr._IsVersion = tryattr.IsVersion;
 | 
			
		||||
				if (tryattr.MapType != null) attr.MapType = tryattr.MapType;
 | 
			
		||||
				if (tryattr.DbDefautValue != null) attr.DbDefautValue = tryattr.DbDefautValue;
 | 
			
		||||
			}
 | 
			
		||||
			if (!string.IsNullOrEmpty(attr.Name)) return attr;
 | 
			
		||||
			if (!string.IsNullOrEmpty(attr.OldName)) return attr;
 | 
			
		||||
			if (!string.IsNullOrEmpty(attr.DbType)) return attr;
 | 
			
		||||
			if (attr._IsPrimary != null) return attr;
 | 
			
		||||
			if (attr._IsIdentity != null) return attr;
 | 
			
		||||
			if (attr._IsNullable != null) return attr;
 | 
			
		||||
			if (attr._IsIgnore != null) return attr;
 | 
			
		||||
			if (attr._IsVersion != null) return attr;
 | 
			
		||||
			if (attr.DbDefautValue != null) return attr;
 | 
			
		||||
			return null;
 | 
			
		||||
			ColumnAttribute ret = null;
 | 
			
		||||
			if (!string.IsNullOrEmpty(attr.Name)) ret = attr;
 | 
			
		||||
			if (!string.IsNullOrEmpty(attr.OldName)) ret = attr;
 | 
			
		||||
			if (!string.IsNullOrEmpty(attr.DbType)) ret = attr;
 | 
			
		||||
			if (attr._IsPrimary != null) ret = attr;
 | 
			
		||||
			if (attr._IsIdentity != null) ret = attr;
 | 
			
		||||
			if (attr._IsNullable != null) ret = attr;
 | 
			
		||||
			if (attr._IsIgnore != null) ret = attr;
 | 
			
		||||
			if (attr._IsVersion != null) ret = attr;
 | 
			
		||||
			if (attr.MapType != null) ret = attr;
 | 
			
		||||
			if (attr.DbDefautValue != null) ret = attr;
 | 
			
		||||
			if (ret != null && ret.MapType == null) ret.MapType = proto.PropertyType;
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal string WhereObject(TableInfo table, string aliasAndDot, object dywhere) {
 | 
			
		||||
			if (dywhere == null) return "";
 | 
			
		||||
			var type = dywhere.GetType();
 | 
			
		||||
			var primarys = table.Columns.Values.Where(a => a.Attribute.IsPrimary == true).ToArray();
 | 
			
		||||
			if (primarys.Length == 1 && (type == primarys.First().CsType || type.IsNumberType() && primarys.First().CsType.IsNumberType())) {
 | 
			
		||||
				return $"{aliasAndDot}{this.QuoteSqlName(primarys.First().Attribute.Name)} = {this.FormatSql("{0}", dywhere)}";
 | 
			
		||||
			var primarys = table.Primarys;
 | 
			
		||||
			var pk1 = primarys.FirstOrDefault();
 | 
			
		||||
			if (primarys.Length == 1 && (type == pk1.CsType || type.IsNumberType() && pk1.CsType.IsNumberType())) {
 | 
			
		||||
				return $"{aliasAndDot}{this.QuoteSqlName(pk1.Attribute.Name)} = {this.FormatSql("{0}", Utils.GetDataReaderValue(pk1.Attribute.MapType, dywhere))}";
 | 
			
		||||
			} else if (primarys.Length > 0 && (type == table.Type || type.BaseType == table.Type)) {
 | 
			
		||||
				var sb = new StringBuilder();
 | 
			
		||||
				var pkidx = 0;
 | 
			
		||||
				foreach (var pk in primarys) {
 | 
			
		||||
					var prop = type.GetProperty(pk.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
 | 
			
		||||
					if (pkidx > 0) sb.Append(" AND ");
 | 
			
		||||
					sb.Append(aliasAndDot).Append(this.QuoteSqlName(pk.Attribute.Name));
 | 
			
		||||
					sb.Append(this.FormatSql(" = {0}", prop.GetValue(dywhere)));
 | 
			
		||||
					sb.Append(this.FormatSql(" = {0}", pk.GetMapValue(dywhere)));
 | 
			
		||||
					++pkidx;
 | 
			
		||||
				}
 | 
			
		||||
				return sb.ToString();
 | 
			
		||||
@@ -165,7 +171,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
					if (table.Columns.TryGetValue(p.Name, out var trycol) == false) continue;
 | 
			
		||||
					if (psidx > 0) sb.Append(" AND ");
 | 
			
		||||
					sb.Append(aliasAndDot).Append(this.QuoteSqlName(trycol.Attribute.Name));
 | 
			
		||||
					sb.Append(this.FormatSql(" = {0}", p.GetValue(dywhere)));
 | 
			
		||||
					sb.Append(this.FormatSql(" = {0}", Utils.GetDataReaderValue(trycol.Attribute.MapType, p.GetValue(dywhere))));
 | 
			
		||||
					++psidx;
 | 
			
		||||
				}
 | 
			
		||||
				if (psidx == 0) return "";
 | 
			
		||||
@@ -178,14 +184,14 @@ namespace FreeSql.Internal {
 | 
			
		||||
			if (table.Primarys.Any() == false) return null;
 | 
			
		||||
			var its = items.Where(a => a != null).ToArray();
 | 
			
		||||
 | 
			
		||||
			var pk1 = table.Primarys.FirstOrDefault();
 | 
			
		||||
			if (table.Primarys.Length == 1) {
 | 
			
		||||
				var sbin = new StringBuilder();
 | 
			
		||||
				sbin.Append(aliasAndDot).Append(this.QuoteSqlName(table.Primarys.First().Attribute.Name));
 | 
			
		||||
				var indt = its.Select(a => /*this.FormatSql("{0}", _orm.GetEntityKeyValues(a))*/
 | 
			
		||||
					table.Properties.TryGetValue(table.Primarys.First().CsName, out var trycol) ? this.FormatSql("{0}", trycol.GetValue(a)) : null).Where(a => a != null).ToArray();
 | 
			
		||||
				sbin.Append(aliasAndDot).Append(this.QuoteSqlName(pk1.Attribute.Name));
 | 
			
		||||
				var indt = its.Select(a => pk1.GetMapValue(a)).Where(a => a != null).ToArray();
 | 
			
		||||
				if (indt.Any() == false) return null;
 | 
			
		||||
				if (indt.Length == 1) sbin.Append(" = ").Append(indt.First());
 | 
			
		||||
				else sbin.Append(" IN (").Append(string.Join(",", indt)).Append(")");
 | 
			
		||||
				if (indt.Length == 1) sbin.Append(" = ").Append(this.FormatSql("{0}", indt.First()));
 | 
			
		||||
				else sbin.Append(" IN (").Append(string.Join(",", indt.Select(a => this.FormatSql("{0}", a)))).Append(")");
 | 
			
		||||
				return sbin.ToString();
 | 
			
		||||
			}
 | 
			
		||||
			var dicpk = its.Length > 5 ? new Dictionary<string, bool>() : null;
 | 
			
		||||
@@ -193,10 +199,8 @@ namespace FreeSql.Internal {
 | 
			
		||||
			var iidx = 0;
 | 
			
		||||
			foreach (var item in its) {
 | 
			
		||||
				var filter = "";
 | 
			
		||||
				for (var a = 0; a < table.Primarys.Length; a++) {
 | 
			
		||||
					if (table.Properties.TryGetValue(table.Primarys[a].CsName, out var trycol) == false) continue;
 | 
			
		||||
					filter += $" AND {aliasAndDot}{this.QuoteSqlName(table.Primarys[a].Attribute.Name)} = {this.FormatSql("{0}", trycol.GetValue(item))}";
 | 
			
		||||
				}
 | 
			
		||||
				foreach (var pk in table.Primarys)
 | 
			
		||||
					filter += $" AND {aliasAndDot}{this.QuoteSqlName(pk.Attribute.Name)} = {this.FormatSql("{0}", pk.GetMapValue(item))}";
 | 
			
		||||
				if (string.IsNullOrEmpty(filter)) continue;
 | 
			
		||||
				if (sb != null) {
 | 
			
		||||
					sb.Append(" OR (");
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,8 @@
 | 
			
		||||
using FreeSql.DataAnnotations;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
 | 
			
		||||
namespace FreeSql.Internal.Model {
 | 
			
		||||
	public class ColumnInfo {
 | 
			
		||||
@@ -7,5 +10,64 @@ namespace FreeSql.Internal.Model {
 | 
			
		||||
		public string CsName { get; set; }
 | 
			
		||||
		public Type CsType { get; set; }
 | 
			
		||||
		public ColumnAttribute Attribute { get; set; }
 | 
			
		||||
 | 
			
		||||
		static ConcurrentDictionary<ColumnInfo, Func<object, object>> _dicGetMapValue = new ConcurrentDictionary<ColumnInfo, Func<object, object>>();
 | 
			
		||||
		public object GetMapValue(object obj) {
 | 
			
		||||
			var func = _dicGetMapValue.GetOrAdd(this, col => {
 | 
			
		||||
				var paramExp = Expression.Parameter(typeof(object));
 | 
			
		||||
				var returnTarget = Expression.Label(typeof(object));
 | 
			
		||||
 | 
			
		||||
				if (Attribute.MapType == CsType)
 | 
			
		||||
					return Expression.Lambda<Func<object, object>>(
 | 
			
		||||
						Expression.Block(
 | 
			
		||||
							Expression.Return(returnTarget, Expression.Convert(
 | 
			
		||||
								Expression.MakeMemberAccess(
 | 
			
		||||
									Expression.TypeAs(paramExp, col.Table.Type),
 | 
			
		||||
									Table.Properties[col.CsName]
 | 
			
		||||
								), typeof(object))),
 | 
			
		||||
							Expression.Label(returnTarget, Expression.Default(typeof(object)))
 | 
			
		||||
						), new[] { paramExp }).Compile();
 | 
			
		||||
 | 
			
		||||
				var retExp = Expression.Variable(typeof(object), "ret");
 | 
			
		||||
				var blockExp = new List<Expression>();
 | 
			
		||||
				blockExp.AddRange(new Expression[] {
 | 
			
		||||
					Expression.Assign(retExp, Utils.GetDataReaderValueBlockExpression(Attribute.MapType,
 | 
			
		||||
						Expression.MakeMemberAccess(
 | 
			
		||||
							Expression.TypeAs(paramExp, col.Table.Type),
 | 
			
		||||
							Table.Properties[col.CsName]
 | 
			
		||||
						)
 | 
			
		||||
					)),
 | 
			
		||||
					Expression.Return(returnTarget, retExp),
 | 
			
		||||
					Expression.Label(returnTarget, Expression.Default(typeof(object)))
 | 
			
		||||
				});
 | 
			
		||||
				return Expression.Lambda<Func<object, object>>(Expression.Block(new[] { retExp }, blockExp), new[] { paramExp }).Compile();
 | 
			
		||||
			});
 | 
			
		||||
			return func(obj);
 | 
			
		||||
		}
 | 
			
		||||
		static ConcurrentDictionary<ColumnInfo, Action<object, object>> _dicSetMapValue = new ConcurrentDictionary<ColumnInfo, Action<object, object>>();
 | 
			
		||||
		public void SetMapValue(object obj, object val) {
 | 
			
		||||
			var func = _dicSetMapValue.GetOrAdd(this, col => {
 | 
			
		||||
				var objExp = Expression.Parameter(typeof(object), "obj");
 | 
			
		||||
				var valExp = Expression.Parameter(typeof(object), "val");
 | 
			
		||||
 | 
			
		||||
				if (Attribute.MapType == CsType)
 | 
			
		||||
					return Expression.Lambda<Action<object, object>>(
 | 
			
		||||
						Expression.Assign(Expression.MakeMemberAccess(
 | 
			
		||||
							Expression.TypeAs(objExp, col.Table.Type),
 | 
			
		||||
							Table.Properties[col.CsName]
 | 
			
		||||
						), Expression.Convert(
 | 
			
		||||
							valExp, 
 | 
			
		||||
							Attribute.MapType)), objExp, valExp).Compile();
 | 
			
		||||
 | 
			
		||||
				return Expression.Lambda<Action<object, object>>(
 | 
			
		||||
					Expression.Assign(Expression.MakeMemberAccess(
 | 
			
		||||
						Expression.TypeAs(objExp, col.Table.Type),
 | 
			
		||||
						Table.Properties[col.CsName]
 | 
			
		||||
					), Expression.Convert(
 | 
			
		||||
						Utils.GetDataReaderValueBlockExpression(Attribute.MapType, valExp), 
 | 
			
		||||
						Attribute.MapType)), objExp, valExp).Compile();
 | 
			
		||||
			});
 | 
			
		||||
			func(obj, val);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -8,6 +8,7 @@ namespace FreeSql.Internal.Model {
 | 
			
		||||
		public PropertyInfo Property { get; set; }
 | 
			
		||||
		public string CsName { get; set; }
 | 
			
		||||
		public Type CsType { get; set; }
 | 
			
		||||
		public Type MapType { get; set; }
 | 
			
		||||
		public string DbField { get; set; }
 | 
			
		||||
		public ConstructorInfo Consturctor { get; set; }
 | 
			
		||||
		public ReadAnonymousTypeInfoConsturctorType ConsturctorType { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Data.Common;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
@@ -22,7 +23,11 @@ namespace FreeSql.Internal {
 | 
			
		||||
			if (tbc.TryRemove(entity, out var trytb) && trytb?.TypeLazy != null) tbc.TryRemove(trytb.TypeLazy, out var trylz);
 | 
			
		||||
		}
 | 
			
		||||
		internal static TableInfo GetTableByEntity(Type entity, CommonUtils common) {
 | 
			
		||||
			if (entity.FullName.StartsWith("<>f__AnonymousType")) return null;
 | 
			
		||||
			if (entity.FullName.StartsWith("<>f__AnonymousType") ||
 | 
			
		||||
				entity.IsValueType || 
 | 
			
		||||
				entity.IsNullableType() || 
 | 
			
		||||
				entity.NullableTypeOrThis() == typeof(BigInteger)
 | 
			
		||||
				) return null;
 | 
			
		||||
			var tbc = _cacheGetTableByEntity.GetOrAdd(common._orm.Ado.DataType, k1 => new ConcurrentDictionary<Type, TableInfo>()); //区分数据库类型缓存
 | 
			
		||||
			if (tbc.TryGetValue(entity, out var trytb)) return trytb;
 | 
			
		||||
			if (common.CodeFirst.GetDbInfo(entity) != null) return null;
 | 
			
		||||
@@ -49,9 +54,9 @@ namespace FreeSql.Internal {
 | 
			
		||||
			var propsNavObjs = new List<PropertyInfo>();
 | 
			
		||||
			foreach (var p in trytb.Properties.Values) {
 | 
			
		||||
				var setMethod = trytb.Type.GetMethod($"set_{p.Name}");
 | 
			
		||||
				var tp = common.CodeFirst.GetDbInfo(p.PropertyType);
 | 
			
		||||
				//if (tp == null) continue;
 | 
			
		||||
				var colattr = common.GetEntityColumnAttribute(entity, p);
 | 
			
		||||
				var tp = common.CodeFirst.GetDbInfo(colattr?.MapType ?? p.PropertyType);
 | 
			
		||||
				//if (tp == null) continue;
 | 
			
		||||
				if (tp == null && colattr == null) {
 | 
			
		||||
					if (common.CodeFirst.IsLazyLoading) {
 | 
			
		||||
						var getIsVirtual = trytb.Type.GetMethod($"get_{p.Name}")?.IsVirtual;
 | 
			
		||||
@@ -69,8 +74,10 @@ namespace FreeSql.Internal {
 | 
			
		||||
						IsIdentity = false,
 | 
			
		||||
						IsNullable = tp.Value.isnullable ?? true,
 | 
			
		||||
						IsPrimary = false,
 | 
			
		||||
						IsIgnore = false
 | 
			
		||||
						IsIgnore = false,
 | 
			
		||||
						MapType = p.PropertyType
 | 
			
		||||
					};
 | 
			
		||||
				if (colattr._IsNullable == null) colattr._IsNullable = tp.Value.isnullable;
 | 
			
		||||
				if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp?.dbtypeFull ?? "varchar(255)";
 | 
			
		||||
				colattr.DbType = colattr.DbType.ToUpper();
 | 
			
		||||
 | 
			
		||||
@@ -92,10 +99,11 @@ namespace FreeSql.Internal {
 | 
			
		||||
					return tmpLt;
 | 
			
		||||
				});
 | 
			
		||||
				colattr.DbDefautValue = trytb.Properties[p.Name].GetValue(Activator.CreateInstance(trytb.Type));
 | 
			
		||||
				if (colattr.DbDefautValue != null && p.PropertyType != colattr.MapType) colattr.DbDefautValue = Utils.GetDataReaderValue(colattr.MapType, colattr.DbDefautValue);
 | 
			
		||||
				if (colattr.DbDefautValue == null) colattr.DbDefautValue = tp?.defaultValue;
 | 
			
		||||
				if (colattr.IsNullable == false && colattr.DbDefautValue == null)
 | 
			
		||||
					colattr.DbDefautValue = Activator.CreateInstance(p.PropertyType.IsNullableType() ? p.PropertyType.GenericTypeArguments.FirstOrDefault() : p.PropertyType);
 | 
			
		||||
				if (colattr.IsIdentity == true && p.PropertyType.IsNumberType() == false)
 | 
			
		||||
					colattr.DbDefautValue = Activator.CreateInstance(colattr.MapType.IsNullableType() ? colattr.MapType.GenericTypeArguments.FirstOrDefault() : colattr.MapType);
 | 
			
		||||
				if (colattr.IsIdentity == true && colattr.MapType.IsNumberType() == false)
 | 
			
		||||
					colattr.IsIdentity = false;
 | 
			
		||||
				if (setMethod == null) colattr.IsIgnore = true;
 | 
			
		||||
 | 
			
		||||
@@ -114,7 +122,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
			}
 | 
			
		||||
			trytb.VersionColumn = trytb.Columns.Values.Where(a => a.Attribute.IsVersion == true).LastOrDefault();
 | 
			
		||||
			if (trytb.VersionColumn != null) {
 | 
			
		||||
				if (trytb.VersionColumn.CsType.IsNullableType() || trytb.VersionColumn.CsType.IsNumberType() == false)
 | 
			
		||||
				if (trytb.VersionColumn.Attribute.MapType.IsNullableType() || trytb.VersionColumn.Attribute.MapType.IsNumberType() == false)
 | 
			
		||||
					throw new Exception($"属性{trytb.VersionColumn.CsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型,并且不可为 Nullable");
 | 
			
		||||
			}
 | 
			
		||||
			trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary == true).ToArray();
 | 
			
		||||
@@ -145,14 +153,14 @@ namespace FreeSql.Internal {
 | 
			
		||||
						var finddbtbs = common.dbTables.Where(a => string.Compare(a.Name, trytb.CsName, true) == 0 || string.Compare(a.Name, trytb.DbName, true) == 0);
 | 
			
		||||
						foreach (var dbtb in finddbtbs) {
 | 
			
		||||
							foreach (var dbident in dbtb.Identitys) {
 | 
			
		||||
								if (trytb.Columns.TryGetValue(dbident.Name, out var trycol) && trycol.CsType == dbident.CsType ||
 | 
			
		||||
									trytb.ColumnsByCs.TryGetValue(dbident.Name, out trycol) && trycol.CsType == dbident.CsType) {
 | 
			
		||||
								if (trytb.Columns.TryGetValue(dbident.Name, out var trycol) && trycol.Attribute.MapType == dbident.CsType ||
 | 
			
		||||
									trytb.ColumnsByCs.TryGetValue(dbident.Name, out trycol) && trycol.Attribute.MapType == dbident.CsType) {
 | 
			
		||||
									trycol.Attribute.IsIdentity = true;
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
							foreach (var dbpk in dbtb.Primarys) {
 | 
			
		||||
								if (trytb.Columns.TryGetValue(dbpk.Name, out var trycol) && trycol.CsType == dbpk.CsType ||
 | 
			
		||||
									trytb.ColumnsByCs.TryGetValue(dbpk.Name, out trycol) && trycol.CsType == dbpk.CsType) {
 | 
			
		||||
								if (trytb.Columns.TryGetValue(dbpk.Name, out var trycol) && trycol.Attribute.MapType == dbpk.CsType ||
 | 
			
		||||
									trytb.ColumnsByCs.TryGetValue(dbpk.Name, out trycol) && trycol.Attribute.MapType == dbpk.CsType) {
 | 
			
		||||
									trycol.Attribute.IsPrimary = true;
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
@@ -682,8 +690,8 @@ 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, int[] indexes, DbDataReader row, int dataIndex, CommonUtils _commonUtils) {
 | 
			
		||||
			var func = _dicExecuteArrayRowReadClassOrTuple.GetOrAdd(type, s => {
 | 
			
		||||
		internal static RowInfo ExecuteArrayRowReadClassOrTuple(Type typeOrg, int[] indexes, DbDataReader row, int dataIndex, CommonUtils _commonUtils) {
 | 
			
		||||
			var func = _dicExecuteArrayRowReadClassOrTuple.GetOrAdd(typeOrg, type => {
 | 
			
		||||
				var returnTarget = Expression.Label(typeof(RowInfo));
 | 
			
		||||
				var typeExp = Expression.Parameter(typeof(Type), "type");
 | 
			
		||||
				var indexesExp = Expression.Parameter(typeof(int[]), "indexes");
 | 
			
		||||
@@ -813,24 +821,25 @@ namespace FreeSql.Internal {
 | 
			
		||||
					});
 | 
			
		||||
					foreach (var ctorParm in ctorParms) {
 | 
			
		||||
						if (typetb.ColumnsByCsIgnore.ContainsKey(ctorParm.Name)) continue;
 | 
			
		||||
						var readType = typetb.ColumnsByCs.TryGetValue(ctorParm.Name, out var trycol) ? trycol.Attribute.MapType : ctorParm.ParameterType;
 | 
			
		||||
 | 
			
		||||
						var ispkExp = new List<Expression>();
 | 
			
		||||
						Expression readVal = Expression.Assign(readpkvalExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp));
 | 
			
		||||
						Expression readExpAssign = null; //加速缓存
 | 
			
		||||
						if (ctorParm.ParameterType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
							GetDataReaderValueBlockExpression(ctorParm.ParameterType, readpkvalExp),
 | 
			
		||||
							//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), readpkvalExp }),
 | 
			
		||||
						if (readType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
							GetDataReaderValueBlockExpression(readType, readpkvalExp),
 | 
			
		||||
							//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(readType), readpkvalExp }),
 | 
			
		||||
							Expression.Add(dataIndexExp, Expression.Constant(1))
 | 
			
		||||
						);
 | 
			
		||||
						else {
 | 
			
		||||
							var proptypeGeneric = ctorParm.ParameterType;
 | 
			
		||||
							var proptypeGeneric = readType;
 | 
			
		||||
							if (proptypeGeneric.IsNullableType()) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First();
 | 
			
		||||
							if (proptypeGeneric.IsEnum ||
 | 
			
		||||
								dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) {
 | 
			
		||||
 | 
			
		||||
								//判断主键为空,则整个对象不读取
 | 
			
		||||
								//blockExp.Add(Expression.Assign(readpkvalExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)));
 | 
			
		||||
								if (typetb.ColumnsByCs.TryGetValue(ctorParm.Name, out var trycol) && trycol.Attribute.IsPrimary == true) {
 | 
			
		||||
								if (trycol?.Attribute.IsPrimary == true) {
 | 
			
		||||
									ispkExp.Add(
 | 
			
		||||
										Expression.IfThen(
 | 
			
		||||
											Expression.AndAlso(
 | 
			
		||||
@@ -846,19 +855,22 @@ namespace FreeSql.Internal {
 | 
			
		||||
								}
 | 
			
		||||
 | 
			
		||||
								readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
									GetDataReaderValueBlockExpression(ctorParm.ParameterType, readpkvalExp),
 | 
			
		||||
									//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(ctorParm.ParameterType), readpkvalExp }),
 | 
			
		||||
									GetDataReaderValueBlockExpression(readType, readpkvalExp),
 | 
			
		||||
									//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(readType), readpkvalExp }),
 | 
			
		||||
									Expression.Add(dataIndexExp, Expression.Constant(1))
 | 
			
		||||
								);
 | 
			
		||||
							} else {
 | 
			
		||||
								readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
									Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(ctorParm.ParameterType), indexesExp, rowExp, dataIndexExp, commonUtilExp }), RowInfo.PropertyValue),
 | 
			
		||||
									Expression.MakeMemberAccess(Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(readType), indexesExp, rowExp, dataIndexExp, commonUtilExp }), RowInfo.PropertyValue),
 | 
			
		||||
									Expression.Add(dataIndexExp, Expression.Constant(1)));
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						var varctorParm = Expression.Variable(ctorParm.ParameterType, $"ctorParm{ctorParm.Name}");
 | 
			
		||||
						readExpValueParms.Add(varctorParm);
 | 
			
		||||
 | 
			
		||||
						if (trycol != null && trycol.Attribute.MapType != ctorParm.ParameterType)
 | 
			
		||||
							ispkExp.Add(Expression.Assign(readExpValue, GetDataReaderValueBlockExpression(ctorParm.ParameterType, readExpValue)));
 | 
			
		||||
 | 
			
		||||
						ispkExp.Add(
 | 
			
		||||
							Expression.IfThen(
 | 
			
		||||
								Expression.IsFalse(readpknullExp),
 | 
			
		||||
@@ -869,6 +881,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
								)
 | 
			
		||||
							)
 | 
			
		||||
						);
 | 
			
		||||
 | 
			
		||||
						blockExp.AddRange(new Expression[] {
 | 
			
		||||
							Expression.Assign(tryidxExp, dataIndexExp),
 | 
			
		||||
							readVal,
 | 
			
		||||
@@ -900,25 +913,26 @@ namespace FreeSql.Internal {
 | 
			
		||||
					var propIndex = 0;
 | 
			
		||||
					foreach (var prop in props) {
 | 
			
		||||
						if (typetb.ColumnsByCsIgnore.ContainsKey(prop.Name)) continue;
 | 
			
		||||
						var readType = typetb.ColumnsByCs.TryGetValue(prop.Name, out var trycol) ? trycol.Attribute.MapType : prop.PropertyType;
 | 
			
		||||
 | 
			
		||||
						var ispkExp = new List<Expression>();
 | 
			
		||||
						var propGetSetMethod = prop.GetSetMethod();
 | 
			
		||||
						Expression readVal = Expression.Assign(readpkvalExp, Expression.Call(rowExp, MethodDataReaderGetValue, tryidxExp));
 | 
			
		||||
						Expression readExpAssign = null; //加速缓存
 | 
			
		||||
						if (prop.PropertyType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
							GetDataReaderValueBlockExpression(prop.PropertyType, readpkvalExp),
 | 
			
		||||
							//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), readpkvalExp }),
 | 
			
		||||
						if (readType.IsArray) readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
							GetDataReaderValueBlockExpression(readType, readpkvalExp),
 | 
			
		||||
							//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(readType), readpkvalExp }),
 | 
			
		||||
							Expression.Add(tryidxExp, Expression.Constant(1))
 | 
			
		||||
						);
 | 
			
		||||
						else {
 | 
			
		||||
							var proptypeGeneric = prop.PropertyType;
 | 
			
		||||
							var proptypeGeneric = readType;
 | 
			
		||||
							if (proptypeGeneric.IsNullableType()) proptypeGeneric = proptypeGeneric.GenericTypeArguments.First();
 | 
			
		||||
							if (proptypeGeneric.IsEnum ||
 | 
			
		||||
								dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) {
 | 
			
		||||
 | 
			
		||||
								//判断主键为空,则整个对象不读取
 | 
			
		||||
								//blockExp.Add(Expression.Assign(readpkvalExp, Expression.Call(rowExp, MethodDataReaderGetValue, dataIndexExp)));
 | 
			
		||||
								if (typetb.ColumnsByCs.TryGetValue(prop.Name, out var trycol) && trycol.Attribute.IsPrimary == true) {
 | 
			
		||||
								if (trycol?.Attribute.IsPrimary == true) {
 | 
			
		||||
									ispkExp.Add(
 | 
			
		||||
										Expression.IfThen(
 | 
			
		||||
											Expression.AndAlso(
 | 
			
		||||
@@ -937,17 +951,20 @@ namespace FreeSql.Internal {
 | 
			
		||||
								}
 | 
			
		||||
 | 
			
		||||
								readExpAssign = Expression.New(RowInfo.Constructor,
 | 
			
		||||
									GetDataReaderValueBlockExpression(prop.PropertyType, readpkvalExp),
 | 
			
		||||
									//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), readpkvalExp }),
 | 
			
		||||
									GetDataReaderValueBlockExpression(readType, readpkvalExp),
 | 
			
		||||
									//Expression.Call(MethodGetDataReaderValue, new Expression[] { Expression.Constant(readType), readpkvalExp }),
 | 
			
		||||
									Expression.Add(tryidxExp, Expression.Constant(1))
 | 
			
		||||
								);
 | 
			
		||||
							} else {
 | 
			
		||||
								++propIndex;
 | 
			
		||||
								continue;
 | 
			
		||||
								//readExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(prop.PropertyType), indexesExp, rowExp, tryidxExp });
 | 
			
		||||
								//readExpAssign = Expression.Call(MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(readType), indexesExp, rowExp, tryidxExp });
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						if (trycol != null && trycol.Attribute.MapType != prop.PropertyType)
 | 
			
		||||
							ispkExp.Add(Expression.Assign(readExpValue, GetDataReaderValueBlockExpression(prop.PropertyType, readExpValue)));
 | 
			
		||||
 | 
			
		||||
						ispkExp.Add(
 | 
			
		||||
							Expression.IfThen(
 | 
			
		||||
								Expression.IsFalse(readpknullExp),
 | 
			
		||||
@@ -986,7 +1003,7 @@ namespace FreeSql.Internal {
 | 
			
		||||
				return Expression.Lambda<Func<Type, int[], DbDataReader, int, CommonUtils, RowInfo>>(
 | 
			
		||||
					Expression.Block(new[] { retExp, readExp, tryidxExp, readpknullExp, readpkvalExp, readExpsIndex, indexesLengthExp }.Concat(readExpValueParms), blockExp), new[] { typeExp, indexesExp, rowExp, dataIndexExp, commonUtilExp }).Compile();
 | 
			
		||||
			});
 | 
			
		||||
			return func(type, indexes, row, dataIndex, _commonUtils);
 | 
			
		||||
			return func(typeOrg, indexes, row, dataIndex, _commonUtils);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal static MethodInfo MethodExecuteArrayRowReadClassOrTuple = typeof(Utils).GetMethod("ExecuteArrayRowReadClassOrTuple", BindingFlags.Static | BindingFlags.NonPublic);
 | 
			
		||||
@@ -1012,16 +1029,24 @@ namespace FreeSql.Internal {
 | 
			
		||||
			act(info, value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static BigInteger ToBigInteger(string that) {
 | 
			
		||||
			if (string.IsNullOrEmpty(that)) return 0;
 | 
			
		||||
			if (BigInteger.TryParse(that, System.Globalization.NumberStyles.Any, null, out var trybigint)) return trybigint;
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		static string ToStringConcat(object obj) {
 | 
			
		||||
			if (obj == null) return null;
 | 
			
		||||
			return string.Concat(obj);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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 MethodGuidTryParse = typeof(Guid).GetMethod("TryParse", new[] { typeof(string), typeof(Guid).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodEnumParse = typeof(Enum).GetMethod("Parse", new[] { typeof(Type), typeof(string), typeof(bool) });
 | 
			
		||||
		static MethodInfo MethodToString = typeof(string).GetMethod("Concat", new[] { typeof(object) });
 | 
			
		||||
		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) });
 | 
			
		||||
		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) });
 | 
			
		||||
@@ -1036,8 +1061,11 @@ namespace FreeSql.Internal {
 | 
			
		||||
		static MethodInfo MethodDoubleTryParse = typeof(double).GetMethod("TryParse", new[] { typeof(string), typeof(double).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodFloatTryParse = typeof(float).GetMethod("TryParse", new[] { typeof(string), typeof(float).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodDecimalTryParse = typeof(decimal).GetMethod("TryParse", new[] { typeof(string), typeof(decimal).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodTimeSpanTryParse = typeof(TimeSpan).GetMethod("TryParse", new[] { typeof(string), typeof(TimeSpan).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodDateTimeTryParse = typeof(DateTime).GetMethod("TryParse", new[] { typeof(string), typeof(DateTime).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodDateTimeOffsetTryParse = typeof(DateTimeOffset).GetMethod("TryParse", new[] { typeof(string), typeof(DateTimeOffset).MakeByRefType() });
 | 
			
		||||
		static MethodInfo MethodToString = typeof(Utils).GetMethod("ToStringConcat", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(object) }, null);
 | 
			
		||||
		static MethodInfo MethodBigIntegerParse = typeof(Utils).GetMethod("ToBigInteger", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(string) }, null);
 | 
			
		||||
		public static Expression GetDataReaderValueBlockExpression(Type type, Expression value) {
 | 
			
		||||
			var returnTarget = Expression.Label(typeof(object));
 | 
			
		||||
			var valueExp = Expression.Variable(typeof(object), "locvalue");
 | 
			
		||||
@@ -1088,11 +1116,6 @@ namespace FreeSql.Internal {
 | 
			
		||||
				ParameterExpression tryparseVarExp = null;
 | 
			
		||||
				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)))
 | 
			
		||||
						//);
 | 
			
		||||
						tryparseExp = Expression.Block(
 | 
			
		||||
						   new[] { tryparseVarExp = Expression.Variable(typeof(Guid)) },
 | 
			
		||||
						   new Expression[] {
 | 
			
		||||
@@ -1114,12 +1137,24 @@ namespace FreeSql.Internal {
 | 
			
		||||
					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.Numerics.BigInteger": return Expression.Return(returnTarget, Expression.Convert(Expression.Call(MethodBigIntegerParse, Expression.Call(MethodToString, valueExp)), typeof(object)));
 | 
			
		||||
					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)))
 | 
			
		||||
						);
 | 
			
		||||
						ParameterExpression tryparseVarTsExp, valueStrExp;
 | 
			
		||||
						return Expression.Block(
 | 
			
		||||
							   new[] { tryparseVarExp = Expression.Variable(typeof(double)), tryparseVarTsExp = Expression.Variable(typeof(TimeSpan)), valueStrExp = Expression.Variable(typeof(string)) },
 | 
			
		||||
							   new Expression[] {
 | 
			
		||||
									Expression.Assign(valueStrExp, Expression.Call(MethodToString, valueExp)),
 | 
			
		||||
									Expression.IfThenElse(
 | 
			
		||||
										Expression.IsTrue(Expression.Call(MethodDoubleTryParse, valueStrExp, tryparseVarExp)),
 | 
			
		||||
										Expression.Return(returnTarget, Expression.Convert(Expression.Call(MethodTimeSpanFromSeconds, tryparseVarExp), typeof(object))),
 | 
			
		||||
										Expression.IfThenElse(
 | 
			
		||||
											Expression.IsTrue(Expression.Call(MethodTimeSpanTryParse, valueStrExp, tryparseVarTsExp)),
 | 
			
		||||
											Expression.Return(returnTarget, Expression.Convert(tryparseVarTsExp, typeof(object))),
 | 
			
		||||
											Expression.Return(returnTarget, Expression.Convert(Expression.Default(typeOrg), typeof(object)))
 | 
			
		||||
										)
 | 
			
		||||
									)
 | 
			
		||||
							   }
 | 
			
		||||
						   );
 | 
			
		||||
					case "System.SByte":
 | 
			
		||||
						tryparseExp = Expression.Block(
 | 
			
		||||
						   new[] { tryparseVarExp = Expression.Variable(typeof(sbyte)) },
 | 
			
		||||
@@ -1308,16 +1343,22 @@ namespace FreeSql.Internal {
 | 
			
		||||
						Expression.SwitchCase(tryparseBooleanExp, Expression.Constant(typeof(bool))),
 | 
			
		||||
						Expression.SwitchCase(Expression.Return(returnTarget, Expression.Call(MethodConvertChangeType, valueExp, Expression.Constant(type, typeof(Type)))), Expression.Constant(type))
 | 
			
		||||
					);
 | 
			
		||||
				else if (type == typeof(string))
 | 
			
		||||
					switchExp = Expression.Return(returnTarget, Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(object)));
 | 
			
		||||
				else
 | 
			
		||||
					switchExp = Expression.Return(returnTarget, Expression.Call(MethodConvertChangeType, valueExp, Expression.Constant(type, typeof(Type))));
 | 
			
		||||
 | 
			
		||||
				var defaultRetExp = type == typeof(string) ?
 | 
			
		||||
					Expression.Return(returnTarget, Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(object))) :
 | 
			
		||||
					Expression.Return(returnTarget, Expression.Call(MethodConvertChangeType, valueExp, Expression.Constant(type, typeof(Type))));
 | 
			
		||||
 | 
			
		||||
				return Expression.IfThenElse(
 | 
			
		||||
					Expression.TypeEqual(valueExp, type),
 | 
			
		||||
					Expression.Return(returnTarget, valueExp),
 | 
			
		||||
					Expression.IfThenElse(
 | 
			
		||||
						Expression.TypeEqual(valueExp, typeof(string)),
 | 
			
		||||
						switchExp,
 | 
			
		||||
						Expression.Return(returnTarget, Expression.Call(MethodConvertChangeType, valueExp, Expression.Constant(type, typeof(Type))))
 | 
			
		||||
						defaultRetExp
 | 
			
		||||
					)
 | 
			
		||||
				);
 | 
			
		||||
			};
 | 
			
		||||
@@ -1345,132 +1386,6 @@ namespace FreeSql.Internal {
 | 
			
		||||
				return Expression.Lambda<Func<object, object>>(exp, parmExp).Compile();
 | 
			
		||||
			});
 | 
			
		||||
			return func(value);
 | 
			
		||||
			#region oldcode
 | 
			
		||||
			//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.IsNullableType()) 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);
 | 
			
		||||
			#endregion
 | 
			
		||||
		}
 | 
			
		||||
		internal static object GetDataReaderValue22(Type type, object value) {
 | 
			
		||||
			if (value == null || value == DBNull.Value) return Activator.CreateInstance(type);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,280 +0,0 @@
 | 
			
		||||
//using FreeSql.DataAnnotations;
 | 
			
		||||
//using FreeSql.Internal.Model;
 | 
			
		||||
//using Newtonsoft.Json.Linq;
 | 
			
		||||
//using Npgsql.LegacyPostgis;
 | 
			
		||||
//using NpgsqlTypes;
 | 
			
		||||
//using System;
 | 
			
		||||
//using System.Collections;
 | 
			
		||||
//using System.Collections.Concurrent;
 | 
			
		||||
//using System.Collections.Generic;
 | 
			
		||||
//using System.Diagnostics;
 | 
			
		||||
//using System.Linq;
 | 
			
		||||
//using System.Net;
 | 
			
		||||
//using System.Net.NetworkInformation;
 | 
			
		||||
//using System.Reflection;
 | 
			
		||||
//using System.Text.RegularExpressions;
 | 
			
		||||
//using System.Threading;
 | 
			
		||||
 | 
			
		||||
//namespace FreeSql.Internal {
 | 
			
		||||
//	class UtilsReflection {
 | 
			
		||||
 | 
			
		||||
//		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;
 | 
			
		||||
 | 
			
		||||
//			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,
 | 
			
		||||
//					};
 | 
			
		||||
//				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 != true || colattr.IsIdentity == true || colattr.IsPrimary == true) && 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)
 | 
			
		||||
//					colattr.DbDefautValue = Activator.CreateInstance(p.PropertyType.IsNullableType() ? p.PropertyType.GenericTypeArguments.FirstOrDefault() : p.PropertyType);
 | 
			
		||||
 | 
			
		||||
//				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 == true).ToArray();
 | 
			
		||||
//			if (trytb.Primarys.Any() == false) {
 | 
			
		||||
//				trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity == true).ToArray();
 | 
			
		||||
//				foreach(var col in trytb.Primarys)
 | 
			
		||||
//					col.Attribute.IsPrimary = true;
 | 
			
		||||
//			}
 | 
			
		||||
//			_cacheGetTableByEntity.TryAdd(entity.FullName, trytb);
 | 
			
		||||
//			return trytb;
 | 
			
		||||
//		}
 | 
			
		||||
 | 
			
		||||
//		internal static T[] GetDbParamtersByObject<T>(string sql, object obj, string paramPrefix, Func<string, Type, object, T> constructorParamter) {
 | 
			
		||||
//			if (string.IsNullOrEmpty(sql) || obj == null) return new T[0];
 | 
			
		||||
//			var ttype = typeof(T);
 | 
			
		||||
//			var type = obj.GetType();
 | 
			
		||||
//			if (type == ttype) return new[] { (T)Convert.ChangeType(obj, type) };
 | 
			
		||||
//			var ret = new List<T>();
 | 
			
		||||
//			var ps = type.GetProperties();
 | 
			
		||||
//			foreach (var p in ps) {
 | 
			
		||||
//				if (sql.IndexOf($"{paramPrefix}{p.Name}", StringComparison.CurrentCultureIgnoreCase) == -1) continue;
 | 
			
		||||
//				var pvalue = p.GetValue(obj);
 | 
			
		||||
//				if (p.PropertyType == ttype) ret.Add((T)Convert.ChangeType(pvalue, ttype));
 | 
			
		||||
//				else ret.Add(constructorParamter(p.Name, p.PropertyType, pvalue));
 | 
			
		||||
//			}
 | 
			
		||||
//			return ret.ToArray();
 | 
			
		||||
//		}
 | 
			
		||||
 | 
			
		||||
//		static Dictionary<Type, bool> dicExecuteArrayRowReadClassOrTuple = new Dictionary<Type, bool> {
 | 
			
		||||
//			[typeof(bool)] = true,
 | 
			
		||||
//			[typeof(sbyte)] = true,
 | 
			
		||||
//			[typeof(short)] = true,
 | 
			
		||||
//			[typeof(int)] = true,
 | 
			
		||||
//			[typeof(long)] = true,
 | 
			
		||||
//			[typeof(byte)] = true,
 | 
			
		||||
//			[typeof(ushort)] = true,
 | 
			
		||||
//			[typeof(uint)] = true,
 | 
			
		||||
//			[typeof(ulong)] = true,
 | 
			
		||||
//			[typeof(double)] = true,
 | 
			
		||||
//			[typeof(float)] = true,
 | 
			
		||||
//			[typeof(decimal)] = true,
 | 
			
		||||
//			[typeof(TimeSpan)] = true,
 | 
			
		||||
//			[typeof(DateTime)] = true,
 | 
			
		||||
//			[typeof(DateTimeOffset)] = true,
 | 
			
		||||
//			[typeof(byte[])] = true,
 | 
			
		||||
//			[typeof(string)] = true,
 | 
			
		||||
//			[typeof(Guid)] = true,
 | 
			
		||||
//			[typeof(MygisPoint)] = true,
 | 
			
		||||
//			[typeof(MygisLineString)] = true,
 | 
			
		||||
//			[typeof(MygisPolygon)] = true,
 | 
			
		||||
//			[typeof(MygisMultiPoint)] = true,
 | 
			
		||||
//			[typeof(MygisMultiLineString)] = true,
 | 
			
		||||
//			[typeof(MygisMultiPolygon)] = true,
 | 
			
		||||
//			[typeof(BitArray)] = true,
 | 
			
		||||
//			[typeof(NpgsqlPoint)] = true,
 | 
			
		||||
//			[typeof(NpgsqlLine)] = true,
 | 
			
		||||
//			[typeof(NpgsqlLSeg)] = true,
 | 
			
		||||
//			[typeof(NpgsqlBox)] = true,
 | 
			
		||||
//			[typeof(NpgsqlPath)] = true,
 | 
			
		||||
//			[typeof(NpgsqlPolygon)] = true,
 | 
			
		||||
//			[typeof(NpgsqlCircle)] = true,
 | 
			
		||||
//			[typeof((IPAddress Address, int Subnet))] = true,
 | 
			
		||||
//			[typeof(IPAddress)] = true,
 | 
			
		||||
//			[typeof(PhysicalAddress)] = true,
 | 
			
		||||
//			[typeof(NpgsqlRange<int>)] = true,
 | 
			
		||||
//			[typeof(NpgsqlRange<long>)] = true,
 | 
			
		||||
//			[typeof(NpgsqlRange<decimal>)] = true,
 | 
			
		||||
//			[typeof(NpgsqlRange<DateTime>)] = true,
 | 
			
		||||
//			[typeof(PostgisPoint)] = true,
 | 
			
		||||
//			[typeof(PostgisLineString)] = true,
 | 
			
		||||
//			[typeof(PostgisPolygon)] = true,
 | 
			
		||||
//			[typeof(PostgisMultiPoint)] = true,
 | 
			
		||||
//			[typeof(PostgisMultiLineString)] = true,
 | 
			
		||||
//			[typeof(PostgisMultiPolygon)] = true,
 | 
			
		||||
//			[typeof(PostgisGeometry)] = true,
 | 
			
		||||
//			[typeof(PostgisGeometryCollection)] = true,
 | 
			
		||||
//			[typeof(Dictionary<string, string>)] = true,
 | 
			
		||||
//			[typeof(JToken)] = true,
 | 
			
		||||
//			[typeof(JObject)] = true,
 | 
			
		||||
//			[typeof(JArray)] = true,
 | 
			
		||||
//		};
 | 
			
		||||
 | 
			
		||||
//		internal static ConcurrentDictionary<Type, _dicClassConstructorInfo> _dicClassConstructor = new ConcurrentDictionary<Type, _dicClassConstructorInfo>();
 | 
			
		||||
//		internal static ConcurrentDictionary<Type, _dicTupleConstructorInfo> _dicTupleConstructor = new ConcurrentDictionary<Type, _dicTupleConstructorInfo>();
 | 
			
		||||
//		internal class _dicClassConstructorInfo {
 | 
			
		||||
//			public ConstructorInfo Constructor { get; set; }
 | 
			
		||||
//			public PropertyInfo[] Properties { get; set; }
 | 
			
		||||
//		}
 | 
			
		||||
//		internal class _dicTupleConstructorInfo {
 | 
			
		||||
//			public ConstructorInfo Constructor { get; set; }
 | 
			
		||||
//			public Type[] Types { get; set; }
 | 
			
		||||
//		}
 | 
			
		||||
//		internal static (object value, int dataIndex) ExecuteArrayRowReadClassOrTuple(Type type, Dictionary<string, int> names, object[] row, int dataIndex = 0) {
 | 
			
		||||
//			if (type.IsArray) return (GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
 | 
			
		||||
//			var typeGeneric = type;
 | 
			
		||||
//			if (typeGeneric.IsNullableType()) typeGeneric = type.GenericTypeArguments.First();
 | 
			
		||||
//			if (typeGeneric.IsEnum ||
 | 
			
		||||
//				dicExecuteArrayRowReadClassOrTuple.ContainsKey(typeGeneric))
 | 
			
		||||
//				return (GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
 | 
			
		||||
//			if (type.Namespace == "System" && (type.FullName == "System.String" || type.IsValueType)) { //值类型,或者元组
 | 
			
		||||
//				bool isTuple = type.Name.StartsWith("ValueTuple`");
 | 
			
		||||
//				if (isTuple) {
 | 
			
		||||
//					if (_dicTupleConstructor.TryGetValue(type, out var tupleInfo) == false) {
 | 
			
		||||
//						var types = type.GetFields().Select(a => a.FieldType).ToArray();
 | 
			
		||||
//						tupleInfo = new _dicTupleConstructorInfo { Constructor = type.GetConstructor(types), Types = types };
 | 
			
		||||
//						_dicTupleConstructor.AddOrUpdate(type, tupleInfo, (t2, c2) => tupleInfo);
 | 
			
		||||
//					}
 | 
			
		||||
//					var parms = new object[tupleInfo.Types.Length];
 | 
			
		||||
//					for (int a = 0; a < parms.Length; a++) {
 | 
			
		||||
//						var read = ExecuteArrayRowReadClassOrTuple(tupleInfo.Types[a], names, row, dataIndex);
 | 
			
		||||
//						if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
 | 
			
		||||
//						parms[a] = read.value;
 | 
			
		||||
//					}
 | 
			
		||||
//					return (tupleInfo.Constructor?.Invoke(parms), dataIndex);
 | 
			
		||||
//				}
 | 
			
		||||
//				return (dataIndex >= row.Length || (row[dataIndex] ?? DBNull.Value) == DBNull.Value ? null : GetDataReaderValue(type, row[dataIndex]), dataIndex + 1);
 | 
			
		||||
//			}
 | 
			
		||||
//			if (type == typeof(object) && names != null) {
 | 
			
		||||
//				dynamic expando = new System.Dynamic.ExpandoObject(); //动态类型字段 可读可写
 | 
			
		||||
//				var expandodic = (IDictionary<string, object>)expando;
 | 
			
		||||
//				foreach (var name in names)
 | 
			
		||||
//					expandodic.Add(name.Key, row[name.Value]);
 | 
			
		||||
//				return (expando, names.Count);
 | 
			
		||||
//			}
 | 
			
		||||
//			//类注入属性
 | 
			
		||||
//			if (_dicClassConstructor.TryGetValue(type, out var classInfo)== false) {
 | 
			
		||||
//				classInfo = new _dicClassConstructorInfo { Constructor = type.GetConstructor(new Type[0]), Properties = type.GetProperties() };
 | 
			
		||||
//				_dicClassConstructor.TryAdd(type, classInfo);
 | 
			
		||||
//			}
 | 
			
		||||
//			var value = classInfo.Constructor.Invoke(new object[0]);
 | 
			
		||||
//			foreach(var prop in classInfo.Properties) {
 | 
			
		||||
//				var tryidx = dataIndex;
 | 
			
		||||
//				if (names != null && names.TryGetValue(prop.Name, out tryidx) == false) continue;
 | 
			
		||||
//				var read = ExecuteArrayRowReadClassOrTuple(prop.PropertyType, names, row, tryidx);
 | 
			
		||||
//				if (read.dataIndex > dataIndex) dataIndex = read.dataIndex;
 | 
			
		||||
//				prop.SetValue(value, read.value, null);
 | 
			
		||||
//				//FillPropertyValue(value, p.Name, read.value);
 | 
			
		||||
//				//p.SetValue(value, read.value);
 | 
			
		||||
//			}
 | 
			
		||||
//			return (value, dataIndex);
 | 
			
		||||
//		}
 | 
			
		||||
 | 
			
		||||
//		internal static void FillPropertyValue(object info, string memberAccessPath, object value) {
 | 
			
		||||
//			var current = info;
 | 
			
		||||
//			PropertyInfo prop = null;
 | 
			
		||||
//			var members = memberAccessPath.Split('.');
 | 
			
		||||
//			for (var a = 0; a < members.Length; a++) {
 | 
			
		||||
//				var type = current.GetType();
 | 
			
		||||
//				prop = type.GetProperty(members[a], BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
 | 
			
		||||
//				if (prop == null) throw new Exception(string.Concat(type.FullName, " 没有定义属性 ", members[a]));
 | 
			
		||||
//				if (a < members.Length - 1) current = prop.GetValue(current);
 | 
			
		||||
//			}
 | 
			
		||||
//			prop.SetValue(current, GetDataReaderValue(prop.PropertyType, value), null);
 | 
			
		||||
//		}
 | 
			
		||||
//		internal static object GetDataReaderValue(Type type, object value) {
 | 
			
		||||
//			if (value == null || value == DBNull.Value) return null;
 | 
			
		||||
//			if (type.FullName == "System.Byte[]") return value;
 | 
			
		||||
//			if (type.IsArray) {
 | 
			
		||||
//				var elementType = type.GetElementType();
 | 
			
		||||
//				var valueArr = value as Array;
 | 
			
		||||
//				if (elementType == valueArr.GetType().GetElementType()) return value;
 | 
			
		||||
//				var len = valueArr.GetLength(0);
 | 
			
		||||
//				var ret = Array.CreateInstance(elementType, len);
 | 
			
		||||
//				for (var a = 0; a < len; a++) {
 | 
			
		||||
//					var item = valueArr.GetValue(a);
 | 
			
		||||
//					ret.SetValue(GetDataReaderValue(elementType, item), a);
 | 
			
		||||
//				}
 | 
			
		||||
//				return ret;
 | 
			
		||||
//			}
 | 
			
		||||
//			if (type.IsNullableType()) type = type.GenericTypeArguments.First();
 | 
			
		||||
//			if (type.IsEnum) return Enum.Parse(type, string.Concat(value), true);
 | 
			
		||||
//			switch(type.FullName) {
 | 
			
		||||
//				case "System.Guid":
 | 
			
		||||
//					if (value.GetType() != type) return Guid.TryParse(string.Concat(value), out var tryguid) ? tryguid : Guid.Empty;
 | 
			
		||||
//					return value;
 | 
			
		||||
//				case "MygisPoint": return MygisPoint.Parse(string.Concat(value)) as MygisPoint;
 | 
			
		||||
//				case "MygisLineString": return MygisLineString.Parse(string.Concat(value)) as MygisLineString;
 | 
			
		||||
//				case "MygisPolygon": return MygisPolygon.Parse(string.Concat(value)) as MygisPolygon;
 | 
			
		||||
//				case "MygisMultiPoint": return MygisMultiPoint.Parse(string.Concat(value)) as MygisMultiPoint;
 | 
			
		||||
//				case "MygisMultiLineString": return MygisMultiLineString.Parse(string.Concat(value)) as MygisMultiLineString;
 | 
			
		||||
//				case "MygisMultiPolygon": return MygisMultiPolygon.Parse(string.Concat(value)) as MygisMultiPolygon;
 | 
			
		||||
//				case "Newtonsoft.Json.Linq.JToken": return JToken.Parse(string.Concat(value));
 | 
			
		||||
//				case "Newtonsoft.Json.Linq.JObject": return JObject.Parse(string.Concat(value));
 | 
			
		||||
//				case "Newtonsoft.Json.Linq.JArray": return JArray.Parse(string.Concat(value));
 | 
			
		||||
//				case "Npgsql.LegacyPostgis.PostgisGeometry": return value;
 | 
			
		||||
//			}
 | 
			
		||||
//			if (type != value.GetType()) {
 | 
			
		||||
//				if (type.FullName == "System.TimeSpan") return TimeSpan.FromSeconds(double.Parse(value.ToString()));
 | 
			
		||||
//				return Convert.ChangeType(value, type);
 | 
			
		||||
//			}
 | 
			
		||||
//			return value;
 | 
			
		||||
//		}
 | 
			
		||||
//		internal static string GetCsName(string name) {
 | 
			
		||||
//			name = Regex.Replace(name.TrimStart('@'), @"[^\w]", "_");
 | 
			
		||||
//			return char.IsLetter(name, 0) ? name : string.Concat("_", name);
 | 
			
		||||
//		}
 | 
			
		||||
//	}
 | 
			
		||||
//}
 | 
			
		||||
		Reference in New Issue
	
	Block a user