using FreeSql.DataAnnotations;
using FreeSql.Internal.CommonProvider;
using FreeSql.Internal.Model;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Common;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace FreeSql.Internal
{
public abstract class BaseDiyMemberExpression
{
///
/// 临时 LambdaExpression.Parameter
///
public ParameterExpression _lambdaParameter;
public ReadAnonymousTypeInfo _map;
public string _field;
public ReadAnonymousTypeInfo ParseExpMapResult { get; protected set; }
public abstract string ParseExp(Expression[] members);
}
public abstract class CommonExpression
{
public CommonUtils _common;
public AdoProvider _ado => _adoPriv ?? (_adoPriv = _common._orm.Ado as AdoProvider);
AdoProvider _adoPriv;
public CommonExpression(CommonUtils common)
{
_common = common;
}
internal const int ReadAnonymousFieldAsCsName = -53129;
internal const int ReadAnonymousFieldAsCsNameGroupBy = -10000;
internal string GetFieldAsCsName(string csname)
{
csname = _common.QuoteSqlName(csname);
if (_common.CodeFirst.IsSyncStructureToLower) csname = csname.ToLower();
if (_common.CodeFirst.IsSyncStructureToUpper) csname = csname.ToUpper();
return csname;
}
internal bool EndsWithDbNestedField(string dbField, string dbNestedField)
{
if (string.IsNullOrWhiteSpace(dbNestedField)) return true;
switch (_ado.DataType)
{
case DataType.SqlServer:
case DataType.OdbcSqlServer:
case DataType.CustomSqlServer:
return dbField.EndsWith(dbNestedField, StringComparison.CurrentCultureIgnoreCase);
}
return dbField.EndsWith(dbNestedField);
}
public bool ReadAnonymousField(List _tables, Func _tableRule, StringBuilder field, ReadAnonymousTypeInfo parent, ref int index, Expression exp, Select0Provider select,
BaseDiyMemberExpression diymemexp, List whereGlobalFilter, List findIncludeMany, List findSubSelectMany, bool isAllDtoMap)
{
void LocalSetFieldAlias(ref int localIndex, bool isdiymemexp)
{
if (localIndex >= 0)
{
parent.DbNestedField = $"as{++localIndex}";
field.Append(_common.FieldAsAlias(parent.DbNestedField));
}
else if (isdiymemexp && diymemexp?.ParseExpMapResult != null)
{
parent.DbNestedField = diymemexp.ParseExpMapResult.DbNestedField;
if (EndsWithDbNestedField(parent.DbField, $" {parent.DbNestedField}") == false && //#1510 group by 产生的 DbField 自带 alias,因此需要此行判断
string.IsNullOrEmpty(parent.CsName) == false && localIndex == ReadAnonymousFieldAsCsName)
{
parent.DbNestedField = GetFieldAsCsName(parent.CsName);
if (EndsWithDbNestedField(parent.DbField, parent.DbNestedField) == false) //DbField 和 CsName 相同的时候,不处理
field.Append(_common.FieldAsAlias(parent.DbNestedField));
}
}
else if (string.IsNullOrEmpty(parent.CsName) == false)
{
parent.DbNestedField = GetFieldAsCsName(parent.CsName);
if (localIndex == ReadAnonymousFieldAsCsName && EndsWithDbNestedField(parent.DbField, parent.DbNestedField) == false) //DbField 和 CsName 相同的时候,不处理
field.Append(_common.FieldAsAlias(parent.DbNestedField));
}
}
Func getTSC = () => new ExpTSC { _tables = _tables, _tableRule = _tableRule, diymemexp = diymemexp, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where, whereGlobalFilter = whereGlobalFilter, dbParams = select?._params }; //#462 添加 DbParams 解决
switch (exp.NodeType)
{
case ExpressionType.Quote: return ReadAnonymousField(_tables, _tableRule, field, parent, ref index, (exp as UnaryExpression)?.Operand, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
case ExpressionType.Lambda: return ReadAnonymousField(_tables, _tableRule, field, parent, ref index, (exp as LambdaExpression)?.Body, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
parent.DbField = $"-({ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, getTSC())})";
field.Append(", ").Append(parent.DbField);
LocalSetFieldAlias(ref index, false);
if (parent.CsType == null && exp.Type.IsValueType) parent.CsType = exp.Type;
return false;
case ExpressionType.Convert: return ReadAnonymousField(_tables, _tableRule, field, parent, ref index, (exp as UnaryExpression)?.Operand, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
case ExpressionType.Constant:
var constExp = exp as ConstantExpression;
//处理自定义SQL语句,如: ToList(new {
// ccc = "now()",
// partby = "sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)"
//}),有缺点即 ccc partby 接受类型都是 string,可配合 Convert.ToXxx 类型转换,请看下面的兼容
if (constExp.Type.FullName == "System.String")
{
var constExpValue = constExp.Value?.ToString() ?? "NULL";
if (constExpValue == string.Empty) constExpValue = _common.FormatSql("{0}", "");
parent.DbField = constExpValue;
}
else
parent.DbField = _common.FormatSql("{0}", constExp?.Value);
field.Append(", ").Append(parent.DbField);
LocalSetFieldAlias(ref index, false);
if (parent.CsType == null && exp.Type.IsValueType) parent.CsType = exp.Type;
return false;
case ExpressionType.Conditional:
var condExp = exp as ConditionalExpression;
if (condExp.Test.IsParameter() == false) return ReadAnonymousField(_tables, _tableRule, field, parent, ref index,
(bool)Expression.Lambda(condExp.Test).Compile().DynamicInvoke() ? condExp.IfTrue : condExp.IfFalse, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
break;
case ExpressionType.Call:
var callExp = exp as MethodCallExpression;
if (callExp.Method.Name == "ToList" && callExp.Object?.Type.FullName.StartsWith("FreeSql.ISelect`") == true)
{
parent.SubSelectMany = exp;
parent.CsType = exp.Type.GetGenericArguments().FirstOrDefault();
findSubSelectMany?.Add(exp);
return false;
}
//处理自定义SQL语句,如: ToList(new {
// ccc = Convert.ToDateTime("now()"),
// partby = Convert.ToDecimal("sum(num) over(PARTITION BY server_id,os,rid,chn order by id desc)")
//})
if (callExp.Method?.DeclaringType.FullName == "System.Convert" &&
callExp.Method.Name.StartsWith("To") &&
callExp.Arguments[0].NodeType == ExpressionType.Constant &&
callExp.Arguments[0].Type.FullName == "System.String")
parent.DbField = (callExp.Arguments[0] as ConstantExpression).Value?.ToString() ?? "NULL";
else
parent.DbField = ExpressionLambdaToSql(exp, getTSC());
field.Append(", ").Append(parent.DbField);
LocalSetFieldAlias(ref index, false);
if (parent.CsType == null && exp.Type.IsValueType) parent.CsType = exp.Type;
return false;
case ExpressionType.Parameter:
case ExpressionType.MemberAccess:
if ((_common.GetTableByEntity(exp.Type) != null || exp.Type.IsAnonymousType() && diymemexp != null) &&
//判断 [JsonMap] 并非导航对象
(exp.NodeType == ExpressionType.Parameter || exp is MemberExpression expMem && (
_common.GetTableByEntity(expMem.Expression.Type)?.ColumnsByCs.ContainsKey(expMem.Member.Name) == false ||
expMem.Expression.NodeType == ExpressionType.Parameter && expMem.Expression.Type.IsAnonymousType()) //<>h__TransparentIdentifier 是 Linq To Sql 的类型判断,此时为匿名类型
)
)
{
//加载表所有字段
var map = new List();
ExpressionSelectColumn_MemberAccess(_tables, _tableRule, map, SelectTableInfoType.From, exp, true, diymemexp);
if (map.Any() == false)
{
if (diymemexp != null && diymemexp.ParseExpMapResult != null)
{
var withTempQueryParser = diymemexp as Select0Provider.WithTempQueryParser;
diymemexp.ParseExpMapResult.CopyTo(parent);
foreach (var child in parent.GetAllChilds())
{
if (withTempQueryParser != null)
child.DbField = $"{withTempQueryParser.ParseExpMatchedTable.Alias}.{child.DbNestedField}";
field.Append(", ").Append(child.DbField);
if (index >= 0)
{
child.DbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(child.DbNestedField));
}
}
return false;
}
throw new Exception($"未能加载它的所有成员,不支持解析表达式树 {exp}");
}
var tb = parent.Table = map.First().Table.Table;
parent.CsType = tb.Type;
parent.Consturctor = tb.Type.InternalGetTypeConstructor0OrFirst();
parent.IsEntity = true;
for (var idx = 0; idx < map.Count; idx++)
{
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.Instance),
CsName = map[idx].Column.CsName,
DbField = $"{map[idx].Table.Alias}.{_common.QuoteSqlName(map[idx].Column.Attribute.Name)}",
DbNestedField = _common.QuoteSqlName(map[idx].Column.Attribute.Name),
CsType = map[idx].Column.CsType,
MapType = map[idx].Column.Attribute.MapType
};
field.Append(", ").Append(_common.RereadColumn(map[idx].Column, child.DbField));
if (index >= 0)
{
child.DbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(child.DbNestedField));
}
parent.Childs.Add(child);
}
if (_tables?.Count > 1)
{ //如果下级导航属性被 Include 过,则将他们也查询出来
foreach (var tr in tb.GetAllTableRef())
{
var memtbref = tr.Value;
if (memtbref.Exception != null) continue;
if (tb.Properties.TryGetValue(tr.Key, out var memProp) == false) continue;
switch (memtbref.RefType)
{
case TableRefType.ManyToMany:
case TableRefType.OneToMany:
case TableRefType.PgArrayToMany:
continue;
}
if (_tables.Any(a => a.Alias == $"{map.First().Table.Alias}__{memProp.Name}") == false) continue;
var child = new ReadAnonymousTypeInfo
{
Property = memProp,
CsName = memProp.Name,
CsType = memProp.PropertyType,
MapType = memProp.PropertyType
};
parent.Childs.Add(child);
ReadAnonymousField(_tables, _tableRule, field, child, ref index, Expression.MakeMemberAccess(exp, memProp), select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, false);
}
}
}
else
{
if (_tables != null && select != null && findIncludeMany != null && select._includeToList.Any() && exp.Type.IsGenericType &&
typeof(IEnumerable).IsAssignableFrom(exp.Type) &&
typeof(ICollection<>).MakeGenericType(exp.Type.GetGenericArguments().FirstOrDefault()).IsAssignableFrom(exp.Type))
{
var includeKey = "";
var memExp = exp as MemberExpression;
while (memExp != null)
{
includeKey = $"{memExp.Member.Name}.{includeKey}";
if (memExp.Expression.NodeType == ExpressionType.Parameter) break;
memExp = memExp.Expression as MemberExpression;
}
if (memExp != null && string.IsNullOrEmpty(includeKey) == false)
{
includeKey = includeKey.TrimEnd('.');
if (select._includeInfo.ContainsKey(includeKey))
{
parent.IncludeManyKey = includeKey;
parent.CsType = exp.Type.GetGenericArguments().FirstOrDefault();
findIncludeMany?.Add(includeKey);
return false;
}
}
}
if (diymemexp != null && exp is MemberExpression expMem2 && expMem2.Member.Name == "Key" && expMem2.Expression.Type.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`"))
{
field.Append(diymemexp._field);
string dbNestedField = null;
if (diymemexp._map.Childs.Any() == false) //处理 GroupBy(a => a.Title) ToSql(g => new { tit = a.Key }, FieldAliasOptions.AsProperty) 问题
{
if (index >= 0)
{
dbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(dbNestedField));
}
else if (string.IsNullOrEmpty(parent.CsName) == false)
{
dbNestedField = GetFieldAsCsName(parent.CsName);
if (index == ReadAnonymousFieldAsCsName && EndsWithDbNestedField(diymemexp._field, dbNestedField) == false) //DbField 和 CsName 相同的时候,不处理
field.Append(_common.FieldAsAlias(dbNestedField));
}
}
var parentProp = parent.Property;
diymemexp._map.CopyTo(parent); //可能会清空 parent.DbNestedField、CsName 值
parent.Property = parentProp; //若不加此行,会引用 GroupBy(..).ToList(a => new Dto { key = a.Key }) null 错误,CopyTo 之后 Property 变为 null
if (string.IsNullOrWhiteSpace(dbNestedField) == false)
parent.DbNestedField = dbNestedField;
return false;
}
if (parent.CsType == null) parent.CsType = exp.Type;
try
{
var pdbfield = parent.DbField = ExpressionLambdaToSql(exp, getTSC());
if (parent.MapType == null || _tables?.Any(a => a.Table?.IsRereadSql == true) == true)
{
var findcol = SearchColumnByField(_tables, null, parent.DbField);
if (parent.MapType == null) parent.MapType = findcol?.Attribute.MapType ?? exp.Type;
if (findcol != null) pdbfield = _common.RereadColumn(findcol, pdbfield);
}
field.Append(", ").Append(pdbfield);
LocalSetFieldAlias(ref index, _tables != null ||
SelectGroupingProvider._ParseExpOnlyDbField.Value != pdbfield);
}
finally
{
SelectGroupingProvider._ParseExpOnlyDbField.Value = null;
}
return false;
}
return false;
case ExpressionType.MemberInit:
var initExp = exp as MemberInitExpression;
parent.CsType = initExp.Type;
parent.Consturctor = initExp.NewExpression.Constructor;
if (initExp.NewExpression?.Arguments.Count > 0)
{
//处理构造参数
for (var a = 0; a < initExp.NewExpression.Arguments.Count; a++)
{
var child = new ReadAnonymousTypeInfo
{
Property = null,
CsName = initExp.NewExpression.Members != null ? initExp.NewExpression.Members[a].Name : (initExp.NewExpression.Arguments[a] as MemberExpression)?.Member.Name,
CsType = initExp.NewExpression.Arguments[a].Type,
MapType = initExp.NewExpression.Arguments[a].Type
};
parent.Childs.Add(child);
ReadAnonymousField(_tables, _tableRule, field, child, ref index, initExp.NewExpression.Arguments[a], select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, false);
}
}
else if (isAllDtoMap && _tables != null && _tables.Any() && initExp.NewExpression.Type != _tables.FirstOrDefault().Table.Type)
{
var dicBindings = initExp.Bindings?.Select(a => a.Member.Name).Distinct().ToDictionary(a => a, a => false);
//dto 映射
var dtoProps = initExp.NewExpression.Type.GetPropertiesDictIgnoreCase().Values;
foreach (var dtoProp in dtoProps)
{
foreach (var dtTb in _tables)
{
if (dtTb.Table.ColumnsByCs.TryGetValue(dtoProp.Name, out var trydtocol) == false)
{
if (diymemexp != null && dtTb.Parameter != null)
{
var isBreaked = false;
var dbTbExps = MatchDtoPropertys(dtTb, dtoProp); //嵌套查询临时类,可能匹配到多个 DTO
foreach (var dbTbExp in dbTbExps)
{
if (dbTbExp?.Any() == true)
{
var dbfield = diymemexp.ParseExp(dbTbExp);
if (diymemexp.ParseExpMapResult != null)
{
var diychild = new ReadAnonymousTypeInfo
{
Property = dtoProp,
CsName = dtoProp.Name,
CsType = dbTbExp.LastOrDefault().Type,
MapType = dbTbExp.LastOrDefault().Type
};
parent.Childs.Add(diychild);
diychild.DbField = $"{dtTb.Alias}.{diymemexp.ParseExpMapResult.DbNestedField}";
diychild.DbNestedField = diymemexp.ParseExpMapResult.DbNestedField;
field.Append(", ").Append(diychild.DbField);
if (index >= 0)
{
diychild.DbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(diychild.DbNestedField));
}
isBreaked = true;
break;
}
}
}
if (isBreaked) break;
}
continue;
}
if (trydtocol.Attribute.IsIgnore == true) continue;
if (dicBindings?.ContainsKey(dtoProp.Name) == true) continue;
var child = new ReadAnonymousTypeInfo
{
Property = dtoProp,
CsName = dtoProp.Name,
CsType = trydtocol.CsType, // dtoProp.PropertyType,
MapType = trydtocol.Attribute.MapType
};
parent.Childs.Add(child);
if (dtTb.Parameter != null)
ReadAnonymousField(_tables, _tableRule, field, child, ref index, Expression.Property(
dtTb.Parameter.Type == dtTb.Table.Type ? (Expression)dtTb.Parameter : Expression.TypeAs(dtTb.Parameter, dtTb.Table.Type),
dtTb.Table.Properties[trydtocol.CsName]), select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
else
{
child.DbField = $"{dtTb.Alias}.{_common.QuoteSqlName(trydtocol.Attribute.Name)}";
child.DbNestedField = _common.QuoteSqlName(trydtocol.Attribute.Name);
field.Append(", ").Append(_common.RereadColumn(trydtocol, child.DbField));
if (index >= 0)
{
child.DbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(child.DbNestedField));
}
}
break;
}
}
}
if (initExp.Bindings?.Count > 0)
{
//指定 dto映射
for (var a = 0; a < initExp.Bindings.Count; a++)
{
var initAssignExp = (initExp.Bindings[a] as MemberAssignment);
if (initAssignExp == null) continue;
var child = new ReadAnonymousTypeInfo
{
Property = initExp.Type.GetProperty(initExp.Bindings[a].Member.Name, BindingFlags.Public | BindingFlags.Instance), //#427 不能使用 BindingFlags.IgnoreCase
CsName = initExp.Bindings[a].Member.Name,
CsType = initAssignExp.Expression.Type,
MapType = initAssignExp.Expression.Type
};
if (child.Property == null) child.ReflectionField = initExp.Type.GetField(initExp.Bindings[a].Member.Name, BindingFlags.Public | BindingFlags.Instance);
parent.Childs.Add(child);
ReadAnonymousField(_tables, _tableRule, field, child, ref index, initAssignExp.Expression, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, false);
}
}
if (parent.Childs.Any() == false) throw new Exception(CoreStrings.Mapping_Exception_HasNo_SamePropertyName(initExp.NewExpression.Type.Name));
return true;
case ExpressionType.New:
var newExp = exp as NewExpression;
parent.CsType = newExp.Type;
parent.Consturctor = newExp.Constructor;
if (newExp.Arguments?.Count > 0 &&
(
newExp.Type.IsAnonymousType() ||
newExp.Arguments.Any(a =>
{
if (a.NodeType != ExpressionType.Constant) return true;
var constVal = (a as ConstantExpression)?.Value;
if (constVal == null) return false; //- 修复 实体类拥有构造参数时,ToList\ 映射查询无效的 bug;
if (object.Equals(constVal, a.Type.CreateInstanceGetDefaultValue())) return false;
return true;
})
))
{
//处理构造参数
for (var a = 0; a < newExp.Arguments.Count; a++)
{
var csname = newExp.Members != null ? newExp.Members[a].Name : (newExp.Arguments[a] as MemberExpression)?.Member.Name;
var child = new ReadAnonymousTypeInfo
{
Property = null,
CsName = csname,
CsType = newExp.Arguments[a].Type,
MapType = newExp.Arguments[a].Type
};
parent.Childs.Add(child);
ReadAnonymousField(_tables, _tableRule, field, child, ref index, newExp.Arguments[a], select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, false);
if (child.CsName == null)
child.CsName = csname;
}
}
else
{
parent.IsDefaultCtor = true;
//dto 映射
var dtoProps2 = newExp.Type.GetPropertiesDictIgnoreCase().Values;
foreach (var dtoProp in dtoProps2)
{
foreach (var dtTb in _tables)
{
if (dtTb.Table.ColumnsByCs.TryGetValue(dtoProp.Name, out var trydtocol) == false)
{
if (diymemexp != null && dtTb.Parameter != null)
{
var isBreaked = false;
var dbTbExps = MatchDtoPropertys(dtTb, dtoProp); //嵌套查询临时类,可能匹配到多个 DTO
foreach (var dbTbExp in dbTbExps)
{
if (dbTbExp?.Any() == true)
{
var dbfield = diymemexp.ParseExp(dbTbExp);
if (diymemexp.ParseExpMapResult != null)
{
var diychild = new ReadAnonymousTypeInfo
{
Property = dtoProp,
CsName = dtoProp.Name,
CsType = dbTbExp.LastOrDefault().Type,
MapType = dbTbExp.LastOrDefault().Type
};
parent.Childs.Add(diychild);
diychild.DbField = $"{dtTb.Alias}.{diymemexp.ParseExpMapResult.DbNestedField}";
diychild.DbNestedField = diymemexp.ParseExpMapResult.DbNestedField;
field.Append(", ").Append(diychild.DbField);
if (index >= 0)
{
diychild.DbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(diychild.DbNestedField));
}
isBreaked = true;
break;
}
}
}
if (isBreaked) break;
}
continue;
}
if (trydtocol.Attribute.IsIgnore == true) continue;
var child = new ReadAnonymousTypeInfo
{
Property = dtoProp,
CsName = dtoProp.Name,
CsType = trydtocol.CsType, //dtoProp.PropertyType,
MapType = trydtocol.Attribute.MapType
};
parent.Childs.Add(child);
if (dtTb.Parameter != null)
ReadAnonymousField(_tables, _tableRule, field, child, ref index, Expression.Property(
dtTb.Parameter.Type == dtTb.Table.Type ? (Expression)dtTb.Parameter : Expression.TypeAs(dtTb.Parameter, dtTb.Table.Type),
dtTb.Table.Properties[trydtocol.CsName]), select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
else
{
child.DbField = _common.RereadColumn(trydtocol, $"{dtTb.Alias}.{_common.QuoteSqlName(trydtocol.Attribute.Name)}");
child.DbNestedField = _common.QuoteSqlName(trydtocol.Attribute.Name);
field.Append(", ").Append(child.DbField);
if (index >= 0)
{
child.DbNestedField = $"as{++index}";
field.Append(_common.FieldAsAlias(child.DbNestedField));
}
}
break;
}
}
}
if (parent.Childs.Any() == false) throw new Exception(CoreStrings.Mapping_Exception_HasNo_SamePropertyName(newExp.Type.Name));
return true;
}
parent.DbField = ExpressionLambdaToSql(exp, getTSC()); //解决 new { a = id + 1 } 翻译后 ((id+1)) 问题
field.Append(", ").Append(parent.DbField);
LocalSetFieldAlias(ref index, false);
if (parent.CsType == null && exp.Type.IsValueType) parent.CsType = exp.Type;
return false;
}
public object ReadAnonymous(ReadAnonymousTypeInfo parent, DbDataReader dr, ref int index, bool notRead, ReadAnonymousDbValueRef dbValue, int rowIndex,
List> fillIncludeMany, List> fillSubSelectMany)
{
if (parent.Childs.Any() == false && string.IsNullOrEmpty(parent.IncludeManyKey) && parent.SubSelectMany == null)
{
if (notRead)
{
++index;
if (parent.Property != null)
return Utils.GetDataReaderValue(parent.Property.PropertyType, null);
return Utils.GetDataReaderValue(parent.CsType, null);
}
object objval = Utils.InternalDataReaderGetValue(_common, dr, ++index); // dr.GetValue(++index);
if (dbValue != null) dbValue.DbValue = objval == DBNull.Value ? null : objval;
if (parent.CsType != parent.MapType)
objval = Utils.GetDataReaderValue(parent.MapType, objval);
objval = Utils.GetDataReaderValue(parent.CsType, objval);
if (parent.Property != null && parent.CsType != parent.Property.PropertyType)
objval = Utils.GetDataReaderValue(parent.Property.PropertyType, objval);
if (objval == DBNull.Value) objval = null;
return objval;
}
var ctorParmsLength = 0;
object ret;
if (string.IsNullOrEmpty(parent.IncludeManyKey) == false)
{
if (parent.MapType == typeof(ObservableCollection<>).MakeGenericType(parent.CsType))
ret = parent.MapType.CreateInstanceGetDefaultValue();
else
ret = typeof(List<>).MakeGenericType(parent.CsType).CreateInstanceGetDefaultValue();
fillIncludeMany?.Add(NativeTuple.Create(parent.IncludeManyKey, ret as IList, rowIndex));
}
else if (parent.SubSelectMany != null)
{
ret = typeof(List<>).MakeGenericType(parent.CsType).CreateInstanceGetDefaultValue();
fillSubSelectMany?.Add(NativeTuple.Create(parent.SubSelectMany, ret as IList, rowIndex));
}
else if (parent.IsDefaultCtor || parent.IsEntity || (ctorParmsLength = parent.Consturctor.GetParameters()?.Length ?? 0) == 0)
ret = parent.CsType?.CreateInstanceGetDefaultValue() ?? parent.Consturctor.Invoke(null);
else
{
var ctorParms = new object[ctorParmsLength];
var ctorParmsDefs = parent.Consturctor.GetParameters();
for (var c = 0; c < ctorParmsLength; c++)
ctorParms[c] = ReadAnonymous(parent.Childs[c], dr, ref index, notRead, null, rowIndex, fillIncludeMany, fillSubSelectMany);
ret = parent.Consturctor.Invoke(ctorParms);
}
var isnull = notRead;
for (var b = ctorParmsLength; b < parent.Childs.Count; b++)
{
var dbval = parent.IsEntity ? new ReadAnonymousDbValueRef() : null;
var objval = ReadAnonymous(parent.Childs[b], dr, ref index, notRead, dbval, rowIndex, fillIncludeMany, fillSubSelectMany);
if (isnull == false && parent.IsEntity && dbval.DbValue == null && parent.Table != null && parent.Table.ColumnsByCs.TryGetValue(parent.Childs[b].CsName, out var trycol) && trycol.Attribute.IsPrimary)
isnull = true;
if (isnull == false)
{
var prop = parent.Childs[b].Property;
if (prop?.CanWrite == true) prop.SetValue(ret, objval, null);
else if (prop == null) parent.Childs[b].ReflectionField?.SetValue(ret, objval);
}
}
return isnull ? null : ret;
}
public class ReadAnonymousDbValueRef
{
public object DbValue { get; set; }
}
public ColumnInfo SearchColumnByField(List _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.Table != null && a.Alias == testCol[0]).ToArray();
if (testTb.Length == 1 && testTb[0].Table.Columns.TryGetValue(testCol[1], out var trytstcol) == true)
return trytstcol;
}
}
if (currentTable != null)
{
var testCol = _common.TrimQuoteSqlName(field);
if (currentTable.Columns.TryGetValue(testCol, out var trytstcol))
return trytstcol;
}
return null;
}
public string ExpressionSelectColumn_MemberAccess(List _tables, Func _tableRule, List _selectColumnMap, SelectTableInfoType tbtype, Expression exp, bool isQuoteName, BaseDiyMemberExpression diymemexp)
{
return ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, _tableRule = _tableRule, _selectColumnMap = _selectColumnMap, diymemexp = diymemexp, tbtype = tbtype, isQuoteName = isQuoteName, isDisableDiyParse = false, style = ExpressionStyle.SelectColumns });
}
public string[] ExpressionSelectColumns_MemberAccess_New_NewArrayInit(List _tables, Func _tableRule, Expression exp, bool isQuoteName, BaseDiyMemberExpression diymemexp)
{
switch (exp?.NodeType)
{
case ExpressionType.Quote: return ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, _tableRule, (exp as UnaryExpression)?.Operand, isQuoteName, diymemexp);
case ExpressionType.Lambda: return ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, _tableRule, (exp as LambdaExpression)?.Body, isQuoteName, diymemexp);
case ExpressionType.Convert: return ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, _tableRule, (exp as UnaryExpression)?.Operand, isQuoteName, diymemexp);
case ExpressionType.Constant: return new[] { ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, isQuoteName, diymemexp) };
case ExpressionType.Call:
case ExpressionType.MemberAccess: return ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, isQuoteName, diymemexp).Trim('(', ')', '\'').Split(new[] { "','" }, StringSplitOptions.RemoveEmptyEntries);
case ExpressionType.New:
var newExp = exp as NewExpression;
if (newExp == null) break;
var newExpMembers = new string[newExp.Members.Count];
for (var a = 0; a < newExpMembers.Length; a++) newExpMembers[a] = ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, newExp.Arguments[a], isQuoteName, diymemexp);
return newExpMembers.Distinct().Select(a => a.Trim('\'')).ToArray();
case ExpressionType.NewArrayInit:
var newArr = exp as NewArrayExpression;
if (newArr == null) break;
var newArrMembers = new List();
foreach (var newArrExp in newArr.Expressions) newArrMembers.AddRange(ExpressionSelectColumns_MemberAccess_New_NewArrayInit(_tables, _tableRule, newArrExp, isQuoteName, diymemexp));
return newArrMembers.Distinct().Select(a => a.Trim('\'')).ToArray();
default: throw new ArgumentException(CoreStrings.Unable_Parse_Expression(exp));
}
return new string[0];
}
static readonly Dictionary dicExpressionOperator = new Dictionary() {
{ ExpressionType.OrElse, "OR" },
{ ExpressionType.AndAlso, "AND" },
{ ExpressionType.Or, "|" },
{ ExpressionType.And, "&" },
{ ExpressionType.LeftShift, "<<" },
{ ExpressionType.RightShift, ">>" },
{ ExpressionType.ExclusiveOr, "^" },
{ ExpressionType.Not, "^" },
{ ExpressionType.GreaterThan, ">" },
{ ExpressionType.GreaterThanOrEqual, ">=" },
{ ExpressionType.LessThan, "<" },
{ ExpressionType.LessThanOrEqual, "<=" },
{ ExpressionType.NotEqual, "<>" },
{ ExpressionType.Add, "+" },
{ ExpressionType.Subtract, "-" },
{ ExpressionType.Multiply, "*" },
{ ExpressionType.Divide, "/" },
{ ExpressionType.Modulo, "%" },
{ ExpressionType.Equal, "=" },
};
public string ExpressionWhereLambdaNoneForeignObject(List _tables, Func _tableRule, TableInfo table, List _selectColumnMap, Expression exp, BaseDiyMemberExpression diymemexp, List dbParams)
{
var sql = ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, _tableRule = _tableRule, _selectColumnMap = _selectColumnMap, diymemexp = diymemexp, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where, currentTable = table, dbParams = dbParams });
return GetBoolString(exp, sql);
}
public string ExpressionWhereLambda(List _tables, Func _tableRule, Expression exp, BaseDiyMemberExpression diymemexp, List whereGlobalFilter, List dbParams)
{
if (_tables?.Count > 1)
{
foreach (var tb in _tables)
if (tb.Parameter != null && tb.AliasInit.StartsWith("SP10"))
tb.Alias = tb.Parameter.Name;
}
var sql = ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, _tableRule = _tableRule, diymemexp = diymemexp, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where, whereGlobalFilter = whereGlobalFilter, dbParams = dbParams });
return GetBoolString(exp, sql);
}
static ConcurrentDictionary dicRegexAlias = new ConcurrentDictionary();
public void ExpressionJoinLambda(List _tables, Func _tableRule, SelectTableInfoType tbtype, Expression exp, BaseDiyMemberExpression diymemexp, List whereGlobalFilter)
{
var tbidx = _tables.Count;
if (tbidx > 1)
{
foreach (var tb in _tables)
if (tb.Parameter != null && tb.AliasInit.StartsWith("SP10"))
tb.Alias = tb.Parameter.Name;
}
var sql = ExpressionLambdaToSql(exp, new ExpTSC { _tables = _tables, _tableRule = _tableRule, diymemexp = diymemexp, tbtype = tbtype, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where, whereGlobalFilter = whereGlobalFilter });
sql = GetBoolString(exp, sql);
if (_tables.Count > tbidx)
{
_tables[tbidx].Type = tbtype;
_tables[tbidx].On = sql;
for (var a = tbidx + 1; a < _tables.Count; a++)
_tables[a].Type = SelectTableInfoType.From;
}
else
{
var find = _tables.Where((a, c) => c > 0 &&
(a.Type == tbtype || a.Type == SelectTableInfoType.From || a.Type == SelectTableInfoType.LeftJoin) &&
string.IsNullOrEmpty(a.On) &&
dicRegexAlias.GetOrAdd(a.Alias, alias => new Regex($@"\b{alias}\.", RegexOptions.Compiled)).IsMatch(sql)).LastOrDefault();
if (find != null)
{
find.Type = tbtype;
find.On = sql;
}
}
}
static ConcurrentDictionary _dicExpressionLambdaToSqlAsSelectMethodInfo = new ConcurrentDictionary();
static ConcurrentDictionary _dicExpressionLambdaToSqlAsSelectWhereMethodInfo = new ConcurrentDictionary();
static ConcurrentDictionary _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo = new ConcurrentDictionary();
static ConcurrentDictionary> _dicExpressionLambdaToSqlAsSelectAggMethodInfo = new ConcurrentDictionary>();
internal static ConcurrentDictionary _dicNullableValueProperty = new ConcurrentDictionary();
static ConcurrentDictionary _dicFreeSqlGlobalExtensionsAsSelectExpression = new ConcurrentDictionary();
static MethodInfo MethodDateTimeSubtractDateTime = typeof(DateTime).GetMethod("Subtract", new Type[] { typeof(DateTime) });
static MethodInfo MethodDateTimeSubtractTimeSpan = typeof(DateTime).GetMethod("Subtract", new Type[] { typeof(TimeSpan) });
static MethodInfo MethodMathFloor = typeof(Math).GetMethod("Floor", new Type[] { typeof(double) });
public string GetBoolString(Expression exp, string sql)
{
var isBool = exp.Type.NullableTypeOrThis() == typeof(bool);
if (exp.NodeType == ExpressionType.MemberAccess && isBool && sql.Contains(" IS ") == false && sql.Contains(" = ") == false)
return $"{sql} = {formatSql(true, null, null, null)}";
if (isBool)
return GetBoolString(sql);
return sql;
}
static string GetBoolString(string sql)
{
switch (sql)
{
case "1":
case "'t'": return "1=1";
case "0":
case "'f'": return "1=2";
default: return sql;
}
}
public string ExpressionBinary(string oper, Expression leftExp, Expression rightExp, ExpTSC tsc)
{
if (
leftExp.Type == rightExp.Type &&
leftExp.NodeType == ExpressionType.Convert &&
leftExp is UnaryExpression leftExpUexp &&
leftExpUexp.Operand?.Type.NullableTypeOrThis().IsEnum == true &&
rightExp.NodeType == ExpressionType.Convert &&
rightExp is UnaryExpression rightExpUexp &&
rightExpUexp.Operand?.Type.NullableTypeOrThis().IsEnum == true)
{
leftExp = leftExpUexp.Operand;
rightExp = rightExpUexp.Operand;
}
switch (oper)
{
case "OR":
case "+":
case "-":
if (oper == "+" && (leftExp.Type == typeof(string) || rightExp.Type == typeof(string)))
return _common.StringConcat(new[] { ExpressionLambdaToSql(leftExp, tsc), ExpressionLambdaToSql(rightExp, tsc) }, new[] { leftExp.Type, rightExp.Type });
if (oper == "-" && leftExp.Type.NullableTypeOrThis() == typeof(DateTime))
{
if (rightExp.Type.NullableTypeOrThis() == typeof(DateTime))
return ExpressionLambdaToSql(Expression.Call(leftExp, MethodDateTimeSubtractDateTime, rightExp), tsc);
if (rightExp.Type.NullableTypeOrThis() == typeof(TimeSpan))
return ExpressionLambdaToSql(Expression.Call(leftExp, MethodDateTimeSubtractTimeSpan, rightExp), tsc);
}
if (oper == "OR")
{
var leftBool = ExpressionLambdaToSql(leftExp, tsc);
if (SearchColumnByField(tsc._tables, tsc.currentTable, leftBool) != null) leftBool = $"{leftBool} = {formatSql(true, null, null, null)}";
else leftBool = GetBoolString(leftBool);
var rightBool = ExpressionLambdaToSql(rightExp, tsc);
if (SearchColumnByField(tsc._tables, tsc.currentTable, rightBool) != null) rightBool = $"{rightBool} = {formatSql(true, null, null, null)}";
else rightBool = GetBoolString(rightBool);
return $"({leftBool} {oper} {rightBool})";
}
return $"({ExpressionLambdaToSql(leftExp, tsc)} {oper} {ExpressionLambdaToSql(rightExp, tsc)})";
case "=":
case "<>":
if (leftExp.NodeType == ExpressionType.Call &&
rightExp.NodeType == ExpressionType.Constant)
{
var leftExpCall = leftExp as MethodCallExpression;
//vb 语法,将字符串比较转换为了 CompareString
if (leftExpCall.Method.Name == "CompareString" &&
leftExpCall.Method.DeclaringType?.FullName == "Microsoft.VisualBasic.CompilerServices.Operators" &&
leftExpCall.Arguments.Count == 3 &&
leftExpCall.Arguments[2].Type == typeof(bool) &&
rightExp.Type == typeof(int) &&
(int)(rightExp as ConstantExpression).Value == 0)
return ExpressionBinary(oper, leftExpCall.Arguments[0], leftExpCall.Arguments[1], tsc);
}
var exptb = _common.GetTableByEntity(leftExp.Type);
if (exptb?.Properties.Any() == true) leftExp = Expression.MakeMemberAccess(leftExp, exptb.Properties[(exptb.Primarys.FirstOrDefault() ?? exptb.Columns.FirstOrDefault().Value)?.CsName]);
exptb = _common.GetTableByEntity(leftExp.Type);
if (exptb?.Properties.Any() == true) rightExp = Expression.MakeMemberAccess(rightExp, exptb.Properties[(exptb.Primarys.FirstOrDefault() ?? exptb.Columns.FirstOrDefault().Value).CsName]);
break;
}
Type oldMapType = null;
var left = ExpressionLambdaToSql(leftExp, tsc);
var leftMapColumn = SearchColumnByField(tsc._tables, tsc.currentTable, left);
var isLeftMapType = leftMapColumn != null && new[] { "AND", "OR", "*", "/", "+", "-" }.Contains(oper) == false && (leftMapColumn.Attribute.MapType != rightExp.Type || leftMapColumn.CsType != rightExp.Type);
ColumnInfo rightMapColumn = null;
var isRightMapType = false;
if (isLeftMapType) oldMapType = tsc.SetMapTypeReturnOld(leftMapColumn.Attribute.MapType);
var right = ExpressionLambdaToSql(rightExp, tsc);
if (right != "NULL" && isLeftMapType &&
//判断参数化后的bug
!(right.Contains('@') || right.Contains('?') || right.Contains(':')) &&
//三元表达式后,取消此条件 #184
tsc.mapType != null)
{
var enumType = leftMapColumn.CsType.NullableTypeOrThis();
if (enumType.IsEnum)
{
rightMapColumn = SearchColumnByField(tsc._tables, tsc.currentTable, right);
if (rightMapColumn == null)
right = formatSql(Enum.Parse(enumType, right.StartsWith("N'") ? right.Substring(1).Trim('\'') : right.Trim('\'')), leftMapColumn.Attribute.MapType, leftMapColumn, tsc.dbParams);
}
}
if (leftMapColumn == null)
{
rightMapColumn = SearchColumnByField(tsc._tables, tsc.currentTable, right);
//.Set(a => a.NotTaxTotalCostPrice == report.NotTaxCostPrice * a.CurrentQty) * / + - 解决 report.NotTaxCostPrice 小数点问题
isRightMapType = rightMapColumn != null && new[] { "AND", "OR", "*", "/", "+", "-" }.Contains(oper) == false && (rightMapColumn.Attribute.MapType != leftExp.Type || rightMapColumn.CsType != leftExp.Type);
if (isRightMapType)
{
oldMapType = tsc.SetMapTypeReturnOld(rightMapColumn.Attribute.MapType);
left = ExpressionLambdaToSql(leftExp, tsc);
if (left != "NULL" && isRightMapType &&
//判断参数化后的bug
!(left.Contains('@') || left.Contains('?') || left.Contains(':')) &&
//三元表达式后,取消此条件 #184
tsc.mapType != null)
{
var enumType = rightMapColumn.CsType.NullableTypeOrThis();
if (enumType.IsEnum)
left = formatSql(Enum.Parse(enumType, left.StartsWith("N'") ? left.Substring(1).Trim('\'') : left.Trim('\'')), rightMapColumn.Attribute.MapType, rightMapColumn, tsc.dbParams);
}
}
}
if (leftExp.Type.NullableTypeOrThis() == typeof(bool) && (left.EndsWith(" IS NOT NULL") || left.EndsWith(" IS NULL") || leftExp.NodeType != ExpressionType.MemberAccess && rightExp.NodeType != ExpressionType.MemberAccess))
{
var leftExpCall = leftExp as MethodCallExpression;
if (leftExpCall == null || !(leftExpCall.Method.DeclaringType == typeof(SqlExt) && leftExpCall.Method.Name == nameof(SqlExt.IsNull)))
{
if (oper == "=")
{
var trueVal = formatSql(true, null, null, null);
var falseVal = formatSql(false, null, null, null);
if (left == trueVal) return right;
else if (left == falseVal) return $"not({right})";
else if (right == trueVal) return left;
else if (right == falseVal) return $"not({left})";
}
else if (oper == "<>")
{
var trueVal = formatSql(true, null, null, null);
var falseVal = formatSql(false, null, null, null);
if (left == trueVal) return $"not({right})";
else if (left == falseVal) return right;
else if (right == trueVal) return $"not({left})";
else if (right == falseVal) return left;
}
}
}
if (left == "NULL")
{
var tmp = right;
right = left;
left = tmp;
}
if (right == "NULL") oper = oper == "=" ? " IS " : " IS NOT ";
switch (oper)
{
case "%": return _common.Mod(left, right, leftExp.Type, rightExp.Type);
case "/":
if (leftExp.Type.IsIntegerType() && rightExp.Type.IsIntegerType()) return _common.Div(left, right, leftExp.Type, rightExp.Type);
break;
case "AND":
case "OR":
if (leftMapColumn != null) left = $"{left} = {formatSql(true, null, null, null)}";
else left = GetBoolString(left);
if (rightMapColumn != null) right = $"{right} = {formatSql(true, null, null, null)}";
else right = GetBoolString(right);
break;
}
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
return $"{left} {oper} {right}";
}
static ConcurrentDictionary _dicTypeExistsExpressionCallAttribute = new ConcurrentDictionary();
static ConcurrentDictionary> _dicMethodExistsExpressionCallAttribute = new ConcurrentDictionary>();
static ConcurrentDictionary _dicTypeExpressionCallClassContextFields = new ConcurrentDictionary();
static ThreadLocal> _subSelectParentDiyMemExps = new ThreadLocal>(); //子查询的所有父自定义查询,比如分组之后的子查询
static ConcurrentDictionary _dicSelectMethodToSql = new ConcurrentDictionary();
public string ExpressionLambdaToSql(Expression exp, ExpTSC tsc)
{
if (exp == null) return "";
if (tsc.dbParams != null && tsc.mapColumnTmp != null && tsc.mapColumnTmp.CsType.NullableTypeOrThis() != exp.Type) tsc.SetMapColumnTmp(null);
if (tsc.isDisableDiyParse == false)
{
var args = new Aop.ParseExpressionEventArgs(exp, ukexp => ExpressionLambdaToSql(ukexp, tsc.CloneDisableDiyParse()), tsc._tables);
if (_common._orm.Aop.ParseExpressionHandler != null)
{
_common._orm.Aop.ParseExpressionHandler(this, args);
if (string.IsNullOrEmpty(args.Result) == false) return args.Result;
}
ParseExpressionNoAsSelect(this, args, tsc._tableRule, tsc.whereGlobalFilter);
if (string.IsNullOrEmpty(args.Result) == false) return args.Result;
}
switch (exp.NodeType)
{
case ExpressionType.Not:
var notExp = (exp as UnaryExpression)?.Operand;
if (notExp.Type.IsNumberType())return _common.BitNot(ExpressionLambdaToSql(notExp, tsc)); //位操作
if (notExp.NodeType == ExpressionType.MemberAccess)
{
var notBody = ExpressionLambdaToSql(notExp, tsc);
if (notBody.Contains(" IS NULL")) return notBody.Replace(" IS NULL", " IS NOT NULL");
if (notBody.Contains(" IS NOT NULL")) return notBody.Replace(" IS NOT NULL", " IS NULL");
if (notBody.Contains("=")) return notBody.Replace("=", "!=");
if (notBody.Contains("!=")) return notBody.Replace("!=", "=");
return $"{notBody} = {formatSql(false, null, null, null)}";
}
return $"not({ExpressionLambdaToSql(notExp, tsc)})";
case ExpressionType.Quote: return ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc);
case ExpressionType.Lambda: return ExpressionLambdaToSql((exp as LambdaExpression)?.Body, tsc);
case ExpressionType.Invoke: //#1378
var invokeExp = exp as InvocationExpression;
var invokeReplaceExp = invokeExp.Expression;
var invokeLambdaExp = invokeReplaceExp as LambdaExpression;
if (invokeLambdaExp == null) return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams);
var invokeReplaceVistor = new FreeSql.Internal.CommonExpression.ReplaceVisitor();
var len = Math.Min(invokeExp.Arguments.Count, invokeLambdaExp.Parameters.Count);
for (var a = 0; a < len; a++)
invokeReplaceExp = invokeReplaceVistor.Modify(invokeReplaceExp, invokeLambdaExp.Parameters[a], invokeExp.Arguments[a]);
return ExpressionLambdaToSql(invokeReplaceExp, tsc);
case ExpressionType.TypeAs:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
//var othercExp = ExpressionLambdaToSqlOther(exp, tsc);
//if (string.IsNullOrEmpty(othercExp) == false) return othercExp;
var expOperand = (exp as UnaryExpression)?.Operand;
if (expOperand.Type.NullableTypeOrThis().IsEnum && exp.IsParameter() == false)
return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); //bug: Where(a => a.Id = (int)enum)
return ExpressionLambdaToSql(expOperand, tsc);
case ExpressionType.Negate:
case ExpressionType.NegateChecked: return $"-({ExpressionLambdaToSql((exp as UnaryExpression)?.Operand, tsc)})";
case ExpressionType.Constant: return formatSql((exp as ConstantExpression)?.Value, tsc.mapType, tsc.mapColumnTmp, null);
case ExpressionType.Conditional:
var condExp = exp as ConditionalExpression;
var conditionalTestOldMapType = tsc.SetMapTypeReturnOld(null);
if (condExp.Test.IsParameter())
{
var condExp2 = condExp.Test;
if (condExp2.NodeType == ExpressionType.MemberAccess) condExp2 = Expression.Equal(condExp2, Expression.Constant(true));
var conditionalTestSql = ExpressionLambdaToSql(condExp2, tsc);
tsc.SetMapTypeReturnOld(conditionalTestOldMapType);
var conditionalSql = _common.IIF(conditionalTestSql, ExpressionLambdaToSql(condExp.IfTrue, tsc), ExpressionLambdaToSql(condExp.IfFalse, tsc));
tsc.SetMapTypeReturnOld(null);
return conditionalSql;
}
if ((bool)Expression.Lambda(condExp.Test).Compile().DynamicInvoke())
{
tsc.SetMapTypeReturnOld(conditionalTestOldMapType);
var conditionalSql = ExpressionLambdaToSql(condExp.IfTrue, tsc);
tsc.SetMapTypeReturnOld(null);
return conditionalSql;
}
else
{
tsc.SetMapTypeReturnOld(conditionalTestOldMapType);
var conditionalSql = ExpressionLambdaToSql(condExp.IfFalse, tsc);
tsc.SetMapTypeReturnOld(null);
return conditionalSql;
}
case ExpressionType.Call:
tsc.mapType = null;
var exp3 = exp as MethodCallExpression;
if (exp3.Object == null && (
_dicTypeExistsExpressionCallAttribute.GetOrAdd(exp3.Method.DeclaringType, dttp => dttp.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()) ||
exp3.Method.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()
))
{
var ecc = new ExpressionCallContext
{
_commonExp = this,
_tsc = tsc,
DataType = _ado.DataType,
UserParameters = tsc.dbParams == null ? null : new List(),
FormatSql = obj => formatSql(obj, null, null, null)
};
var exp3MethodParams = exp3.Method.GetParameters();
var dbParamsIndex = tsc.dbParams?.Count;
if (exp3MethodParams.Any())
{
ecc.RawExpression.Add(exp3MethodParams[0].Name, exp3.Arguments[0]);
ecc.ParsedContent.Add(exp3MethodParams[0].Name, exp3MethodParams[0].GetCustomAttributes(typeof(RawValueAttribute), true).Any() ? null : ExpressionLambdaToSql(exp3.Arguments[0], tsc));
}
if (tsc.dbParams?.Count > dbParamsIndex) ecc.DbParameter = tsc.dbParams.Last();
List oldDbParams = tsc.SetDbParamsReturnOld(null);
for (var a = 1; a < exp3.Arguments.Count; a++)
if (exp3.Arguments[a].Type != typeof(ExpressionCallContext))
{
ecc.RawExpression.Add(exp3MethodParams[a].Name, exp3.Arguments[a]);
ecc.ParsedContent.Add(exp3MethodParams[a].Name, exp3MethodParams[a].GetCustomAttributes(typeof(RawValueAttribute), true).Any() ? null : ExpressionLambdaToSql(exp3.Arguments[a], tsc));
}
tsc.SetDbParamsReturnOld(oldDbParams);
var exp3InvokeParams = new object[exp3.Arguments.Count];
for (var a = 0; a < exp3.Arguments.Count; a++)
{
if (exp3.Arguments[a].Type != typeof(ExpressionCallContext))
{
var eccContent = ecc.ParsedContent[exp3MethodParams[a].Name];
if (eccContent == null)
{
var isdyInvoke = true;
if (exp3.Arguments[a].NodeType == ExpressionType.Call) //判断如果参数也是标记 ExpressionCall
{
var exp3ArgsACallExp = exp3.Arguments[a] as MethodCallExpression;
if (exp3ArgsACallExp.Object == null && (
_dicTypeExistsExpressionCallAttribute.GetOrAdd(exp3ArgsACallExp.Method.DeclaringType, dttp => dttp.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()) ||
exp3ArgsACallExp.Method.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()
))
isdyInvoke = false;
}
if (isdyInvoke)
exp3InvokeParams[a] = Expression.Lambda(exp3.Arguments[a]).Compile().DynamicInvoke();
}
else if (exp3.Arguments[a].IsParameter())
exp3InvokeParams[a] = exp3.Arguments[a].Type.CreateInstanceGetDefaultValue();
else if (Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(exp3.Arguments[a].Type.NullableTypeOrThis()) == false)
exp3InvokeParams[a] = exp3.Arguments[a].Type.CreateInstanceGetDefaultValue();
else
{
var exp3CsValue = eccContent.StartsWith("N'") ?
eccContent.Substring(1).Trim('\'').Replace("''", "'") :
eccContent.Trim('\'').Replace("''", "'");
switch (_ado.DataType)
{
case DataType.MySql:
case DataType.OdbcMySql:
case DataType.CustomMySql:
exp3CsValue = exp3CsValue.Replace("\\\\", "\\");
break;
}
exp3InvokeParams[a] = Utils.GetDataReaderValue(exp3.Arguments[a].Type, exp3CsValue);
}
}
else
exp3InvokeParams[a] = ecc;
}
var eccFields = _dicTypeExpressionCallClassContextFields.GetOrAdd(exp3.Method.DeclaringType, dttp =>
dttp.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Static).Where(a => a.FieldType == typeof(ThreadLocal)).ToArray());
if (eccFields.Any() == false)
throw new Exception(CoreStrings.Custom_Expression_ParsingError(exp3.Method.DeclaringType));
foreach (var eccField in eccFields)
typeof(ThreadLocal).GetProperty("Value").SetValue(eccField.GetValue(null), ecc, null);
try
{
var invokeReturn = exp3.Method.Invoke(null, exp3InvokeParams);
if (string.IsNullOrEmpty(ecc.Result) && invokeReturn is string) ecc.Result = string.Concat(invokeReturn);
if (string.IsNullOrEmpty(ecc.Result) && exp3MethodParams.Any()) ecc.Result = ecc.ParsedContent[exp3MethodParams[0].Name];
if (ecc.UserParameters?.Any() == true) tsc.dbParams?.AddRange(ecc.UserParameters);
return ecc.Result;
}
finally
{
foreach (var eccField in eccFields)
typeof(ThreadLocal).GetProperty("Value").SetValue(eccField.GetValue(null), null, null);
}
}
var callType = exp3.Object?.Type ?? exp3.Method.DeclaringType;
string other3Exp = null;
switch (callType.FullName)
{
case "System.String": other3Exp = ExpressionLambdaToSqlCallString(exp3, tsc); break;
case "System.Math": other3Exp = ExpressionLambdaToSqlCallMath(exp3, tsc); break;
case "System.DateTime": other3Exp = ExpressionLambdaToSqlCallDateTime(exp3, tsc); break;
case "System.TimeSpan": other3Exp = ExpressionLambdaToSqlCallTimeSpan(exp3, tsc); break;
case "System.Convert": other3Exp = ExpressionLambdaToSqlCallConvert(exp3, tsc); break;
}
if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp;
if (exp3.Method.Name == "Equals")
{
if (exp3.Arguments.Count > 0 && exp3.Object != null) return ExpressionBinary("=", exp3.Object, exp3.Arguments[0], tsc);
if (exp3.Arguments.Count > 1 && exp3.Method.DeclaringType == typeof(object)) return ExpressionBinary("=", exp3.Arguments[0], exp3.Arguments[1], tsc);
}
if (exp3.Method.Name == "Any" && exp3.Method.DeclaringType == typeof(Enumerable))
{
//Where(a => idArray.Any(p => (a.Id == p.Key || a.RoleName == p.Key) && a.RoleType == p.Type))
var exp3MethodGenArgs = exp3.Method.GetGenericArguments();
var exp3MethodArgs = exp3.Method.GetParameters();
if (exp3MethodGenArgs.Length == 1 && exp3MethodArgs.Length == 2 && exp3MethodArgs[1].ParameterType == typeof(Func<,>).MakeGenericType(exp3MethodGenArgs[0], typeof(bool)))
{
var exp3Value = ExpressionGetValue(exp3.Arguments[0], out var exp3ValueSuccess);
if (exp3ValueSuccess)
{
if (exp3Value == null) return "1=2";
var exp3ValueIE = exp3Value as IEnumerable;
var exp3NewExpVisitor = new ReplaceParameterVisitor();
var exp3sb = new StringBuilder();
foreach (var exp3ValueItem in exp3ValueIE)
{
var exp3NewExp = exp3NewExpVisitor.Modify(exp3.Arguments[1] as LambdaExpression, Expression.Constant(exp3ValueItem, exp3MethodGenArgs[0]));
exp3sb.Append(" OR ").Append(ExpressionLambdaToSql(exp3NewExp, tsc));
}
if (exp3sb.Length == 0) return "1=2";
return $"({exp3sb.Remove(0, 4).ToString()})";
}
}
}
if (callType.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`"))
{
switch (exp3.Method.Name)
{
case "Count": return exp3.Arguments.Count == 0 ? "count(1)" : $"count({ExpressionLambdaToSql(exp3.Arguments[0], tsc)})";
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`"))
{ //子表查询
switch (exp3.Method.Name)
{
case "Any": //exists
case "Count":
case "Sum":
case "Min":
case "Max":
case "Avg":
case "ToList": //where in
case "ToOne":
case "First":
var anyArgs = exp3.Arguments;
var exp3Stack = new Stack();
var exp3tmp = exp3.Object;
if (exp3.Method.Name == "Any" && exp3tmp != null && anyArgs.Any())
exp3Stack.Push(Expression.Call(exp3tmp, callType.GetMethod("Where", anyArgs.Select(a => a.Type).ToArray()), anyArgs.ToArray()));
while (exp3tmp != null)
{
exp3Stack.Push(exp3tmp);
switch (exp3tmp.NodeType)
{
case ExpressionType.Call:
var exp3tmpCall = (exp3tmp as MethodCallExpression);
exp3tmp = exp3tmpCall.Object == null ? exp3tmpCall.Arguments.FirstOrDefault() : exp3tmpCall.Object;
continue;
case ExpressionType.MemberAccess: exp3tmp = (exp3tmp as MemberExpression).Expression; continue;
}
break;
}
object fsql = null;
Expression fsqlExpLambda = null;
Select0Provider fsqlSelect0 = null;
List fsqltables = null;
var fsqltable1SetAlias = false;
var fsqltable1SetAliasGai = 0;
Type fsqlType = null;
Stack asSelectBefores = new Stack();
var asSelectSql = "";
Type asSelectEntityType = null;
MemberExpression asSelectParentExp1 = null;
Expression asSelectParentExp = null;
while (exp3Stack.Any())
{
exp3tmp = exp3Stack.Pop();
if (exp3tmp.Type.FullName.StartsWith("FreeSql.ISelect`") && fsql == null)
{
if (exp3tmp.NodeType == ExpressionType.Call)
{
var exp3tmpCall = (exp3tmp as MethodCallExpression);
if (exp3tmpCall.Method.Name == "AsSelect" && exp3tmpCall.Object == null)
{
var exp3tmpArg1Type = exp3tmpCall.Arguments.FirstOrDefault()?.Type;
if (exp3tmpArg1Type != null)
{
asSelectEntityType = exp3tmpArg1Type.GetElementType() ?? exp3tmpArg1Type.GetGenericArguments().FirstOrDefault();
if (asSelectEntityType != null)
{
fsql = _dicExpressionLambdaToSqlAsSelectMethodInfo.GetOrAdd(asSelectEntityType, asSelectEntityType2 => typeof(IFreeSql).GetMethod("Select", new Type[0]).MakeGenericMethod(asSelectEntityType2))
.Invoke(_common._orm, null);
if (asSelectBefores.Any())
{
asSelectParentExp1 = asSelectBefores.Pop() as MemberExpression;
if (asSelectBefores.Any())
{
asSelectParentExp = asSelectBefores.Pop();
if (asSelectParentExp != null)
{
var testExecuteExp = asSelectParentExp;
if (asSelectParentExp.NodeType == ExpressionType.Parameter) //执行leftjoin关联
testExecuteExp = Expression.Property(testExecuteExp, _common.GetTableByEntity(asSelectParentExp.Type).ColumnsByCs.First().Key);
var tsc2 = tsc.Clone_selectColumnMap_diymemexp_tbtype(new List(), tsc.diymemexp, SelectTableInfoType.LeftJoin);
tsc2.isDisableDiyParse = true;
tsc2.style = ExpressionStyle.AsSelect;
asSelectSql = ExpressionLambdaToSql(testExecuteExp, tsc2);
}
}
}
}
}
}
if (new[] { "Where", "WhereIf" }.Contains(exp3tmpCall.Method.Name) && exp3tmpCall.Object != null)
{
//这段特别兼容 DbSet.Where 表达式解析 #216
var exp3tmpTestCall = Expression.Call(exp3tmpCall.Object, exp3tmpCall.Method, exp3tmpCall.Arguments.Select(a =>
{
var a2 = a;
if (a2.NodeType == ExpressionType.Quote) a2 = (a as UnaryExpression)?.Operand;
if (a2?.NodeType == ExpressionType.Lambda)
{
var alambda = a2 as LambdaExpression;
if (alambda.ReturnType == typeof(bool))
return Expression.Constant(null, a.Type);// Expression.Lambda(Expression.Constant(true), alambda.Parameters);
}
return a;
//if (a.Type == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(exp3tmp.Type.GetGenericArguments()[0], typeof(bool))))
// return Expression.Lambda(Expression.Constant(true),
}).ToArray());
fsql = Expression.Lambda(exp3tmpTestCall).Compile().DynamicInvoke();
var fsqlFindMethod = fsql.GetType().GetMethod(exp3tmpCall.Method.Name, exp3tmpCall.Arguments.Select(a => a.Type).ToArray());
if (fsqlFindMethod == null)
throw new Exception(CoreStrings.Unable_Parse_ExpressionMethod(exp3tmpCall.Method.Name));
var exp3StackOld = exp3Stack;
exp3Stack = new Stack();
exp3Stack.Push(Expression.Call(Expression.Constant(fsql), fsqlFindMethod, exp3tmpCall.Arguments));
while (exp3StackOld.Any()) exp3Stack.Push(exp3StackOld.Pop());
}
}
if (fsql == null)
{
fsql = Expression.Lambda(exp3tmp).Compile().DynamicInvoke();
fsqlExpLambda = exp3tmp;
}
fsqlType = fsql?.GetType();
if (fsqlType == null) break;
fsqlSelect0 = fsql as Select0Provider;
switch (exp3.Method.Name)
{
case "Any": //exists
switch (_ado.DataType)
{
case DataType.Oracle:
case DataType.OdbcOracle:
case DataType.CustomOracle:
case DataType.Dameng:
case DataType.OdbcDameng:
case DataType.GBase:
break;
default:
fsqlSelect0._limit = 1; //#462 ORACLE rownum <= 2 会影响索引变慢
break;
}
break;
case "ToOne":
case "First":
fsqlSelect0._limit = 1; //#462
break;
}
if (tsc.dbParams != null) fsqlSelect0._params = tsc.dbParams;
fsqltables = fsqlSelect0._tables;
//fsqltables[0].Alias = $"{tsc._tables[0].Alias}_{fsqltables[0].Alias}";
if (fsqltables != tsc._tables)
{
if (tsc._tables == null && tsc.diymemexp == null) throw new NotSupportedException(CoreStrings.EspeciallySubquery_Cannot_Parsing); //2020-12-11 IUpdate 条件不支持子查询
if (tsc._tables != null) //groupby is null
{
fsqltables.AddRange(tsc._tables.Select(a => new SelectTableInfo
{
Alias = a.Alias,
On = "1=1",
Table = a.Table,
Type = SelectTableInfoType.Parent,
Parameter = a.Parameter
}));
}
}
if (tsc.whereGlobalFilter != null)
{
fsqlSelect0._whereGlobalFilter.Clear();
if (tsc.whereGlobalFilter.Any()) fsqlSelect0._whereGlobalFilter.AddRange(tsc.whereGlobalFilter);
}
}
else if (fsqlType != null)
{
var call3Exp = exp3tmp as MethodCallExpression;
var method = call3Exp.Method;
//var method = fsqlType.GetMethod(call3Exp.Method.Name, call3Exp.Arguments.Select(a => a.Type).ToArray());
//if (call3Exp.Method.ContainsGenericParameters) method.MakeGenericMethod(call3Exp.Method.GetGenericArguments());
var parms = method.GetParameters();
var args = new object[call3Exp.Arguments.Count];
for (var a = 0; a < args.Length; a++)
{
var arg3Exp = call3Exp.Arguments[a];
if (arg3Exp.NodeType == ExpressionType.Constant)
{
args[a] = (arg3Exp as ConstantExpression)?.Value;
}
else if (arg3Exp == fsqlExpLambda)
{
args[a] = fsql;
}
else
{
var argExp = (arg3Exp as UnaryExpression)?.Operand;
if (argExp != null)
{
if (argExp.NodeType == ExpressionType.Lambda)
{
if (fsqltable1SetAlias == false)
{
fsqltable1SetAlias = true;
var argExpLambda = argExp as LambdaExpression;
var fsqlTypeGenericArgs = fsqlType.GetGenericArguments();
if (argExpLambda.Parameters.Count == 1 && argExpLambda.Parameters[0].Type.FullName.StartsWith("FreeSql.Internal.Model.HzyTuple`"))
{
for (; fsqltable1SetAliasGai < fsqlTypeGenericArgs.Length; fsqltable1SetAliasGai++)
fsqltables[fsqltable1SetAliasGai].Alias = "ht" + (fsqltable1SetAliasGai + 1);
}
else
{
for (; fsqltable1SetAliasGai < fsqlTypeGenericArgs.Length && fsqltable1SetAliasGai < argExpLambda.Parameters.Count; fsqltable1SetAliasGai++)
{
var alias = argExpLambda.Parameters[fsqltable1SetAliasGai].Name;
if (fsqltables.Any(x => x.Type == SelectTableInfoType.Parent && x.Alias == alias)) alias = $"sub_{alias}";
fsqltables[fsqltable1SetAliasGai].Alias = alias;
}
}
}
}
else
{
argExp = null;
}
}
args[a] = argExp ?? Expression.Lambda(arg3Exp).Compile().DynamicInvoke();
//if (args[a] == null) ExpressionLambdaToSql(call3Exp.Arguments[a], fsqltables, null, null, SelectTableInfoType.From, true);
}
}
var isSubSelectPdme = tsc._tables == null && tsc.diymemexp != null || tsc.diymemexp is Select0Provider.WithTempQueryParser;
try
{
if (isSubSelectPdme)
{
if (_subSelectParentDiyMemExps.Value == null) _subSelectParentDiyMemExps.Value = new List();
_subSelectParentDiyMemExps.Value.Add(tsc.diymemexp);
}
switch (method.Name)
{
case nameof(ISelect