using FreeSql.Internal.Model; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { public abstract partial class Select0Provider { public int _limit, _skip; public string _select = "SELECT ", _orderby, _groupby, _having; public StringBuilder _where = new StringBuilder(); public List _params = new List(); public List _tables = new List(); public List> _tableRules = new List>(); public Func _aliasRule; public string _tosqlAppendContent; public StringBuilder _join = new StringBuilder(); public IFreeSql _orm; public CommonUtils _commonUtils; public CommonExpression _commonExpression; public DbTransaction _transaction; public DbConnection _connection; public Action _trackToList; public List> _includeToList = new List>(); #if net40 #else public List> _includeToListAsync = new List>(); #endif public bool _distinct; public Expression _selectExpression; public List _whereCascadeExpression = new List(); public List _whereGlobalFilter; int _disposeCounter; ~Select0Provider() { if (Interlocked.Increment(ref _disposeCounter) != 1) return; _where.Clear(); _params.Clear(); _tables.Clear(); _tableRules.Clear(); _join.Clear(); _trackToList = null; _includeToList.Clear(); #if net40 #else _includeToListAsync.Clear(); #endif _selectExpression = null; _whereCascadeExpression.Clear(); _whereGlobalFilter = _orm.GlobalFilter.GetFilters(); _whereCascadeExpression.AddRange(_whereGlobalFilter.Select(a => a.Where)); } public static void CopyData(Select0Provider from, Select0Provider to, ReadOnlyCollection lambParms) { if (to == null) return; to._limit = from._limit; to._skip = from._skip; to._select = from._select; to._orderby = from._orderby; to._groupby = from._groupby; to._having = from._having; to._where = new StringBuilder().Append(from._where.ToString()); to._params = new List(from._params.ToArray()); if (lambParms == null) to._tables = new List(from._tables.ToArray()); else { var findedIndexs = new List(); var _multiTables = to._tables; _multiTables[0] = from._tables[0]; for (var a = 1; a < lambParms.Count; a++) { var tbIndex = from._tables.FindIndex(b => b.Alias == lambParms[a].Name && b.Table.Type == lambParms[a].Type); ; if (tbIndex != -1) { findedIndexs.Add(tbIndex); _multiTables[a] = from._tables[tbIndex]; } else { _multiTables[a].Alias = lambParms[a].Name; _multiTables[a].Parameter = lambParms[a]; } } for (var a = 1; a < from._tables.Count; a++) { if (findedIndexs.Contains(a)) continue; _multiTables.Add(from._tables[a]); } } to._tableRules = new List>(from._tableRules.ToArray()); to._aliasRule = from._aliasRule; to._join = new StringBuilder().Append(from._join.ToString()); //to._orm = from._orm; //to._commonUtils = from._commonUtils; //to._commonExpression = from._commonExpression; to._transaction = from._transaction; to._connection = from._connection; to._trackToList = from._trackToList; to._includeToList = new List>(from._includeToList.ToArray()); #if net40 #else to._includeToListAsync = new List>(from._includeToListAsync.ToArray()); #endif to._distinct = from._distinct; to._selectExpression = from._selectExpression; to._whereCascadeExpression = new List(from._whereCascadeExpression.ToArray()); to._whereGlobalFilter = new List(from._whereGlobalFilter.ToArray()); } } public abstract partial class Select0Provider : Select0Provider, ISelect0 where TSelect : class where T1 : class { public Select0Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) { _orm = orm; _commonUtils = commonUtils; _commonExpression = commonExpression; _tables.Add(new SelectTableInfo { Table = _commonUtils.GetTableByEntity(typeof(T1)), Alias = "a", On = null, Type = SelectTableInfoType.From }); this.Where(_commonUtils.WhereObject(_tables.First().Table, "a.", dywhere)); if (_orm.CodeFirst.IsAutoSyncStructure && typeof(T1) != typeof(object)) _orm.CodeFirst.SyncStructure(); } public TSelect TrackToList(Action track) { _trackToList = track; return this as TSelect; } public TSelect WithTransaction(DbTransaction transaction) { _transaction = transaction; _connection = _transaction?.Connection; return this as TSelect; } public TSelect WithConnection(DbConnection connection) { if (_transaction?.Connection != connection) _transaction = null; _connection = connection; return this as TSelect; } public bool Any() { this.Limit(1); return this.ToList($"{1}{_commonUtils.FieldAsAlias("as1")}").Sum() > 0; //这里的 Sum 为了分表查询 } public long Count() { var tmpOrderBy = _orderby; _orderby = null; //解决 select count(1) from t order by id 这样的 SQL 错误 try { return this.ToList($"count(1){_commonUtils.FieldAsAlias("as1")}").Sum(); //这里的 Sum 为了分表查询 } finally { _orderby = tmpOrderBy; } } public TSelect Count(out long count) { count = this.Count(); return this as TSelect; } public TSelect GroupBy(string sql, object parms = null) { _groupby = sql; if (string.IsNullOrEmpty(_groupby)) return this as TSelect; _groupby = string.Concat(" \r\nGROUP BY ", _groupby); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(_groupby, parms)); return this as TSelect; } public TSelect Having(string sql, object parms = null) { if (string.IsNullOrEmpty(_groupby) || string.IsNullOrEmpty(sql)) return this as TSelect; _having = string.Concat(_having, " AND (", sql, ")"); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect LeftJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin); } public TSelect InnerJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin); } public TSelect RightJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin); } public TSelect LeftJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin); } public TSelect InnerJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin); } public TSelect RightJoin(Expression> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin); } public TSelect InnerJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\nINNER JOIN ").Append(sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect LeftJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\nLEFT JOIN ").Append(sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect RightJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\nRIGHT JOIN ").Append(sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect RawJoin(string sql) { if (string.IsNullOrEmpty(sql)) return this as TSelect; _join.Append(" \r\n").Append(sql); return this as TSelect; } public TSelect Limit(int limit) { _limit = limit; return this as TSelect; } public TSelect Master() { _select = $" {_select.Trim()} "; return this as TSelect; } public TSelect Offset(int offset) => this.Skip(offset) as TSelect; public TSelect OrderBy(string sql, object parms = null) => this.OrderBy(true, sql, parms); public TSelect OrderBy(bool condition, string sql, object parms = null) { if (condition == false) return this as TSelect; if (string.IsNullOrEmpty(sql)) _orderby = null; var isnull = string.IsNullOrEmpty(_orderby); _orderby = string.Concat(isnull ? " \r\nORDER BY " : "", _orderby, isnull ? "" : ", ", sql); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } public TSelect Page(int pageNumber, int pageSize) { this.Skip(Math.Max(0, pageNumber - 1) * pageSize); return this.Limit(pageSize) as TSelect; } public TSelect Skip(int offset) { _skip = offset; return this as TSelect; } public TSelect Take(int limit) => this.Limit(limit) as TSelect; public TSelect Distinct() { _distinct = true; return this as TSelect; } public DataTable ToDataTable(string field = null) { var sql = this.ToSql(field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); DataTable ret = null; Exception exception = null; try { ret = _orm.Ado.ExecuteDataTable(_connection, _transaction, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } public List ToList(string field) { var sql = this.ToSql(field); var type = typeof(TTuple); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); var flagStr = $"ToListField:{field}"; Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, dr => { var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, dr, 0, _commonUtils); ret.Add((TTuple)read.Value); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } internal List ToListAfPrivate(string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, dr => { ret.Add(af.Read(_orm, dr)); if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, dr, ref idx, false, null)); } }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } foreach (var include in _includeToList) include?.Invoke(ret); _trackToList?.Invoke(ret); return ret; } internal List ToListPrivate(GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { string sql = null; if (otherData?.Length > 0) { var sbField = new StringBuilder().Append(af.Field); foreach (var other in otherData) sbField.Append(other.field); sql = this.ToSql(sbField.ToString()); } else sql = this.ToSql(af.Field); return ToListAfPrivate(sql, af, otherData); } #region ToChunk internal void ToListAfChunkPrivate(int chunkSize, Action> chunkDone, string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); var retCount = 0; Exception exception = null; var checkDoneTimes = 0; try { _orm.Ado.ExecuteReader(_connection, _transaction, dr => { ret.Add(af.Read(_orm, dr)); retCount++; if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, dr, ref idx, false, null)); } if (chunkSize > 0 && chunkSize == ret.Count) { checkDoneTimes++; foreach (var include in _includeToList) include?.Invoke(ret); _trackToList?.Invoke(ret); chunkDone(ret); ret.Clear(); if (otherData != null) foreach (var other in otherData) other.retlist.Clear(); } }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, retCount); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (ret.Any() || checkDoneTimes == 0) { foreach (var include in _includeToList) include?.Invoke(ret); _trackToList?.Invoke(ret); chunkDone(ret); } } internal void ToListChunkPrivate(int chunkSize, Action> chunkDone, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { string sql = null; if (otherData?.Length > 0) { var sbField = new StringBuilder().Append(af.Field); foreach (var other in otherData) sbField.Append(other.field); sql = this.ToSql(sbField.ToString()); } else sql = this.ToSql(af.Field); ToListAfChunkPrivate(chunkSize, chunkDone, sql, af, otherData); } public void ToChunk(int size, Action> done, bool includeNestedMembers = false) { if (_selectExpression != null) throw new ArgumentException("Chunk 功能之前不可使用 Select"); this.ToListChunkPrivate(size, done, includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } #endregion public Dictionary ToDictionary(Func keySelector) => ToDictionary(keySelector, a => a); public Dictionary ToDictionary(Func keySelector, Func elementSelector) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector)); var af = this.GetAllFieldExpressionTreeLevel2(); var sql = this.ToSql(af.Field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new Dictionary(); Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, dr => { var item = af.Read(_orm, dr); ret.Add(keySelector(item), elementSelector(item)); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (typeof(TElement) == typeof(T1)) _trackToList?.Invoke(ret.Values); return ret; } public virtual List ToList(bool includeNestedMembers = false) { if (_selectExpression != null) return this.InternalToList(_selectExpression); return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } public T1 ToOne() { this.Limit(1); return this.ToList().FirstOrDefault(); } public T1 First() => this.ToOne(); internal List ToListMrPrivate(string sql, ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData) { var type = typeof(TReturn); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, dr => { var index = -1; ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false, null)); if (otherData != null) foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, dr, ref index, false, null)); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (typeof(TReturn) == typeof(T1)) foreach (var include in _includeToList) include?.Invoke(ret); _trackToList?.Invoke(ret); return ret; } internal List ToListMapReaderPrivate(ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData) { string sql = null; if (otherData?.Length > 0) { var sbField = new StringBuilder().Append(af.field); foreach (var other in otherData) sbField.Append(other.field); sql = this.ToSql(sbField.ToString()); } else sql = this.ToSql(af.field); return ToListMrPrivate(sql, af, otherData); } protected List ToListMapReader(ReadAnonymousTypeAfInfo af) => ToListMapReaderPrivate(af, null); protected ReadAnonymousTypeAfInfo GetExpressionField(Expression newexp, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = fieldAlias == FieldAliasOptions.AsProperty ? CommonExpression.ReadAnonymousFieldAsCsName : 0; _commonExpression.ReadAnonymousField(_tables, field, map, ref index, newexp, null, _whereCascadeExpression, true); return new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); } static ConcurrentDictionary _dicGetAllFieldExpressionTree = new ConcurrentDictionary(); public class GetAllFieldExpressionTreeInfo { public string Field { get; set; } public int FieldCount { get; set; } public Func Read { get; set; } } protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevelAll() { return _dicGetAllFieldExpressionTree.GetOrAdd($"*{string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}"))}", s => { var type = _tables.First().Table.TypeLazy ?? _tables.First().Table.Type; var ormExp = Expression.Parameter(typeof(IFreeSql), "orm"); var rowExp = Expression.Parameter(typeof(DbDataReader), "row"); var returnTarget = Expression.Label(type); var retExp = Expression.Variable(type, "ret"); var dataIndexExp = Expression.Variable(typeof(int), "dataIndex"); var readExp = Expression.Variable(typeof(Utils.RowInfo), "read"); var readExpValue = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyValue); var readExpDataIndex = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyDataIndex); var blockExp = new List(); blockExp.AddRange(new Expression[] { Expression.Assign(retExp, type.InternalNewExpression()), Expression.Assign(dataIndexExp, Expression.Constant(0)) }); //typeof(Topic).GetMethod("get_Type").IsVirtual var field = new StringBuilder(); var dicfield = new Dictionary(); var tb = _tables.First(); var index = 0; var tborder = new[] { tb }.Concat(_tables.ToArray().Where((a, b) => b > 0).OrderBy(a => a.Alias)); var tbiindex = 0; foreach (var tbi in tborder) { if (tbiindex > 0 && tbi.Type == SelectTableInfoType.From) continue; if (tbiindex > 0 && tbi.Alias.StartsWith($"{tb.Alias}__") == false) continue; var typei = tbi.Table.TypeLazy ?? tbi.Table.Type; Expression curExp = retExp; var colidx = 0; foreach (var col in tbi.Table.Columns.Values) { if (index > 0) { field.Append(", "); if (tbiindex > 0 && colidx == 0) field.Append("\r\n"); } var quoteName = _commonUtils.QuoteSqlName(col.Attribute.Name); field.Append(_commonUtils.QuoteReadColumn(col.CsType, col.Attribute.MapType, $"{tbi.Alias}.{quoteName}")); ++index; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else dicfield.Add(quoteName, true); ++colidx; } tbiindex++; if (tbiindex == 0) blockExp.AddRange(new Expression[] { Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(null, typeof(string)), Expression.Constant(typei), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), Expression.IfThen( Expression.GreaterThan(readExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, readExpDataIndex) ), Expression.IfThen( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Assign(retExp, Expression.Convert(readExpValue, typei)) ) }); else { Expression curExpIfNotNull = Expression.IsTrue(Expression.Constant(true)); var curTb = tb; var parentNameSplits = tbi.Alias.Split(new[] { "__" }, StringSplitOptions.None); var iscontinue = false; for (var k = 1; k < parentNameSplits.Length; k++) { var curPropName = parentNameSplits[k]; if (curTb.Table.Properties.TryGetValue(parentNameSplits[k], out var tryprop) == false) { k++; curPropName = $"{curPropName}__{parentNameSplits[k]}"; if (curTb.Table.Properties.TryGetValue(parentNameSplits[k], out tryprop) == false) { iscontinue = true; break; } } curExp = Expression.MakeMemberAccess(curExp, tryprop); if (k + 1 < parentNameSplits.Length) curExpIfNotNull = Expression.AndAlso(curExpIfNotNull, Expression.NotEqual(curExp, Expression.Default(tryprop.PropertyType))); curTb = _tables.Where(a => a.Alias == $"{curTb.Alias}__{curPropName}" && a.Table.Type == tryprop.PropertyType).FirstOrDefault(); if (curTb == null) { iscontinue = true; break; } } if (iscontinue) continue; blockExp.Add( Expression.IfThenElse( curExpIfNotNull, Expression.Block(new Expression[] { Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(null, typeof(string)), Expression.Constant(typei), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), Expression.IfThen( Expression.GreaterThan(readExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, readExpDataIndex) ), Expression.IfThenElse( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Assign(curExp, Expression.Convert(readExpValue, typei)), Expression.Assign(curExp, Expression.Constant(null, typei)) ) }), Expression.Block( Expression.Assign(readExpValue, Expression.Constant(null, typeof(object))), Expression.Assign(dataIndexExp, Expression.Constant(index)) ) ) ); } if (tbi.Table.TypeLazy != null) blockExp.Add( Expression.IfThen( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Call(Expression.TypeAs(readExpValue, typei), tbi.Table.TypeLazySetOrm, ormExp) ) ); //将 orm 传递给 lazy } blockExp.AddRange(new Expression[] { Expression.Return(returnTarget, retExp), Expression.Label(returnTarget, Expression.Default(type)) }); return new GetAllFieldExpressionTreeInfo { Field = field.ToString(), FieldCount = index, Read = Expression.Lambda>(Expression.Block(new[] { retExp, dataIndexExp, readExp }, blockExp), new[] { ormExp, rowExp }).Compile() }; }); } protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevel2() { return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Alias}-{a.Type}")), s => { var tb1 = _tables.First().Table; var type = tb1.TypeLazy ?? tb1.Type; var props = tb1.Properties; var ormExp = Expression.Parameter(typeof(IFreeSql), "orm"); var rowExp = Expression.Parameter(typeof(DbDataReader), "row"); var returnTarget = Expression.Label(type); var retExp = Expression.Variable(type, "ret"); var dataIndexExp = Expression.Variable(typeof(int), "dataIndex"); var readExp = Expression.Variable(typeof(Utils.RowInfo), "read"); var readExpValue = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyValue); var readExpDataIndex = Expression.MakeMemberAccess(readExp, Utils.RowInfo.PropertyDataIndex); var blockExp = new List(); blockExp.AddRange(new Expression[] { Expression.Assign(retExp, type.InternalNewExpression()), Expression.Assign(dataIndexExp, Expression.Constant(0)) }); //typeof(Topic).GetMethod("get_Type").IsVirtual var field = new StringBuilder(); var dicfield = new Dictionary(); var tb = _tables.First(); var index = 0; var otherindex = 0; foreach (var prop in props.Values) { if (tb.Table.ColumnsByCsIgnore.ContainsKey(prop.Name)) continue; 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, col.Attribute.MapType, $"{tb.Alias}.{quoteName}")); ++index; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else dicfield.Add(quoteName, true); } else { var tb2 = _tables.Where((a, b) => b > 0 && (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && string.IsNullOrEmpty(a.On) == false && a.Alias.StartsWith($"{tb.Alias}__") && //开头结尾完全匹配 a.Alias.EndsWith($"__{prop.Name}") //不清楚会不会有其他情况 求大佬优化 ).FirstOrDefault(); //判断 b > 0 防止 parent 递归关系 if (tb2 == null && props.Where(pw => pw.Value.PropertyType == prop.PropertyType).Count() == 1) tb2 = _tables.Where((a, b) => b > 0 && (a.Type == SelectTableInfoType.InnerJoin || a.Type == SelectTableInfoType.LeftJoin || a.Type == SelectTableInfoType.RightJoin) && string.IsNullOrEmpty(a.On) == false && a.Table.Type == prop.PropertyType).FirstOrDefault(); if (tb2 == null) continue; 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, col2.Attribute.MapType, $"{tb2.Alias}.{quoteName}")); ++index; ++otherindex; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else dicfield.Add(quoteName, true); } } //只读到二级属性 var propGetSetMethod = prop.GetSetMethod(true); Expression readExpAssign = null; //加速缓存 if (prop.PropertyType.IsArray) readExpAssign = Expression.New(Utils.RowInfo.Constructor, Utils.GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(Utils.MethodDataReaderGetValue, new Expression[] { Expression.Constant(_commonUtils), rowExp, dataIndexExp })), //Expression.Call(Utils.MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { var proptypeGeneric = prop.PropertyType; if (proptypeGeneric.IsNullableType()) proptypeGeneric = proptypeGeneric.GetGenericArguments().First(); if (proptypeGeneric.IsEnum || Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(proptypeGeneric)) readExpAssign = Expression.New(Utils.RowInfo.Constructor, Utils.GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Call(Utils.MethodDataReaderGetValue, new Expression[] { Expression.Constant(_commonUtils), rowExp, dataIndexExp })), //Expression.Call(Utils.MethodGetDataReaderValue, new Expression[] { Expression.Constant(prop.PropertyType), Expression.Call(rowExp, Utils.MethodDataReaderGetValue, dataIndexExp) }), Expression.Add(dataIndexExp, Expression.Constant(1)) ); else { var propLazyType = _commonUtils.GetTableByEntity(prop.PropertyType)?.TypeLazy ?? prop.PropertyType; readExpAssign = Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(null, typeof(string)), Expression.Constant(propLazyType), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) }); } } blockExp.AddRange(new Expression[] { Expression.Assign(readExp, readExpAssign), Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp), Expression.Assign(dataIndexExp, readExpDataIndex)), //Expression.Call(typeof(Trace).GetMethod("WriteLine", new Type[]{typeof(string)}), Expression.Call(typeof(string).GetMethod("Concat", new Type[]{typeof(object) }), readExpValue)), tb1.TypeLazy != null ? Expression.IfThenElse( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)), Expression.Call(retExp, propGetSetMethod, Expression.Convert(Utils.GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Constant(null)), prop.PropertyType)) ) : Expression.IfThen( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)) ) }); } if (otherindex == 0) { //不读导航属性,优化单表读取性能 blockExp.Clear(); blockExp.AddRange(new Expression[] { Expression.Assign(dataIndexExp, Expression.Constant(0)), Expression.Assign(readExp, Expression.Call(Utils.MethodExecuteArrayRowReadClassOrTuple, new Expression[] { Expression.Constant(null, typeof(string)), Expression.Constant(type), Expression.Constant(null, typeof(int[])), rowExp, dataIndexExp, Expression.Constant(_commonUtils) })), Expression.IfThen( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Assign(retExp, Expression.Convert(readExpValue, type)) ) }); } if (tb1.TypeLazy != null) blockExp.Add( Expression.IfThen( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Call(retExp, tb1.TypeLazySetOrm, ormExp) ) ); //将 orm 传递给 lazy blockExp.AddRange(new Expression[] { Expression.Return(returnTarget, retExp), Expression.Label(returnTarget, Expression.Default(type)) }); return new GetAllFieldExpressionTreeInfo { Field = field.ToString(), Read = Expression.Lambda>(Expression.Block(new[] { retExp, dataIndexExp, readExp }, blockExp), new[] { ormExp, rowExp }).Compile() }; }); } protected ReadAnonymousTypeAfInfo GetAllFieldReflection() { var tb1 = _tables.First().Table; var type = tb1.TypeLazy ?? tb1.Type; var constructor = type.InternalGetTypeConstructor0OrFirst(); var map = new ReadAnonymousTypeInfo { CsType = type, Consturctor = constructor, IsEntity = true }; var field = new StringBuilder(); var dicfield = new Dictionary(); var tb = _tables.First(); var index = 0; var ps = tb1.Properties; foreach (var p in ps.Values) { var child = new ReadAnonymousTypeInfo { Property = p, CsName = p.Name }; 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, col.Attribute.MapType, $"{tb.Alias}.{quoteName}")); ++index; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else dicfield.Add(quoteName, true); } else { var tb2 = _tables.Where(a => a.Table.Type == p.PropertyType && a.Alias.Contains(p.Name)).FirstOrDefault(); if (tb2 == null && ps.Where(pw => pw.Value.PropertyType == p.PropertyType).Count() == 1) tb2 = _tables.Where(a => a.Table.Type == p.PropertyType).FirstOrDefault(); if (tb2 == null) continue; child.CsType = (tb2.Table.TypeLazy ?? tb2.Table.Type); child.Consturctor = child.CsType.InternalGetTypeConstructor0OrFirst(); child.IsEntity = true; 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, col2.Attribute.MapType, $"{tb2.Alias}.{quoteName}")); ++index; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else dicfield.Add(quoteName, true); child.Childs.Add(new ReadAnonymousTypeInfo { Property = tb2.Table.Type.GetProperty(col2.CsName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance), CsName = col2.CsName }); } } map.Childs.Add(child); } return new ReadAnonymousTypeAfInfo(map, field.ToString()); } string GetToDeleteWhere(string alias) { var pks = _tables[0].Table.Primarys; if (pks.Length == 1) return $"{_commonUtils.QuoteSqlName(_tables[0].Table.Primarys[0].Attribute.Name)} in (select * from ({this.ToSql($"{_tables[0].Alias}.{_commonUtils.QuoteSqlName(_tables[0].Table.Primarys[0].Attribute.Name)}")}) {alias})"; else { var concatTypes = new Type[pks.Length * 2 - 1]; var concatMainCols = new string[pks.Length * 2 - 1]; var concatInCols = new string[pks.Length * 2 - 1]; var concatSplit = _commonUtils.FormatSql("{0}", $",{alias},"); for (var a = 0; a < pks.Length; a++) { concatTypes[a * 2] = pks[a].CsType; concatMainCols[a * 2] = _commonUtils.QuoteSqlName(pks[a].Attribute.Name); concatInCols[a * 2] = $"{_tables[0].Alias}.{_commonUtils.QuoteSqlName(pks[a].Attribute.Name)}"; if (a < pks.Length - 1) { concatTypes[a * 2 + 1] = typeof(string); concatMainCols[a * 2 + 1] = concatSplit; concatInCols[a * 2 + 1] = concatSplit; } } return $"{_commonUtils.StringConcat(concatMainCols, concatTypes)} in (select * from ({this.ToSql($"{_commonUtils.StringConcat(concatInCols, concatTypes)} as as1")}) {alias})"; } } public IDelete ToDelete() { if (_tables[0].Table.Primarys.Any() == false) throw new Exception($"ToDelete 功能要求实体类 {_tables[0].Table.CsName} 必须有主键"); var del = _orm.Delete(); if (_tables[0].Table.Type != typeof(T1)) del.AsType(_tables[0].Table.Type); if (_params.Any()) del.GetType().GetField("_params", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(del, new List(_params.ToArray())); return del.Where(GetToDeleteWhere("ftb_del")); } public IUpdate ToUpdate() { if (_tables[0].Table.Primarys.Any() == false) throw new Exception($"ToUpdate 功能要求实体类 {_tables[0].Table.CsName} 必须有主键"); var upd = _orm.Update(); if (_tables[0].Table.Type != typeof(T1)) upd.AsType(_tables[0].Table.Type); if (_params.Any()) upd.GetType().GetField("_params", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(upd, new List(_params.ToArray())); return upd.Where(GetToDeleteWhere("ftb_upd")); } protected List> GetTableRuleUnions() { var unions = new List>(); var trs = _tableRules.Any() ? _tableRules : new List>(new[] { new Func((type, oldname) => null) }); foreach (var tr in trs) { var dict = new Dictionary(); foreach (var tb in _tables) { if (tb.Type == SelectTableInfoType.Parent) continue; if (dict.ContainsKey(tb.Table.Type)) continue; var name = tr?.Invoke(tb.Table.Type, tb.Table.DbName); if (string.IsNullOrEmpty(name)) name = tb.Table.DbName; else { if (name.IndexOf(' ') == -1) //还可以这样:select.AsTable((a, b) => "(select * from tb_topic where clicks > 10)").Page(1, 10).ToList() { if (_orm.CodeFirst.IsSyncStructureToLower) name = name.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) name = name.ToUpper(); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(tb.Table.Type, name); } else name = name.Replace("\r\n", "\r\n "); } dict.Add(tb.Table.Type, name); } unions.Add(dict); } return unions; } public TSelect AsTable(Func tableRule) { if (tableRule != null) _tableRules.Add(tableRule); return this as TSelect; } public TSelect AsAlias(Func aliasRule) { if (aliasRule != null) _aliasRule = aliasRule; return this as TSelect; } public TSelect AsType(Type entityType) { if (entityType == typeof(object)) throw new Exception("ISelect.AsType 参数不支持指定为 object"); if (entityType == _tables[0].Table.Type) return this as TSelect; var newtb = _commonUtils.GetTableByEntity(entityType); _tables[0].Table = newtb ?? throw new Exception("ISelect.AsType 参数错误,请传入正确的实体类型"); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); return this as TSelect; } public abstract string ToSql(string field = null); public TSelect Where(string sql, object parms = null) => this.WhereIf(true, sql, parms); public TSelect WhereIf(bool condition, string sql, object parms = null) { if (condition == false || string.IsNullOrEmpty(sql)) return this as TSelect; _where.Append(" AND (").Append(sql).Append(")"); if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } static MethodInfo MethodStringContains = typeof(string).GetMethod("Contains", new[] { typeof(string) }); static MethodInfo MethodStringStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) }); static MethodInfo MethodStringEndsWith = typeof(string).GetMethod("EndsWith", new[] { typeof(string) }); static ConcurrentDictionary MethodEnumerableContainsDic = new ConcurrentDictionary(); static MethodInfo GetMethodEnumerableContains(Type elementType) => MethodEnumerableContainsDic.GetOrAdd(elementType, et => typeof(Enumerable).GetMethods().Where(a => a.Name == "Contains").FirstOrDefault().MakeGenericMethod(elementType)); public TSelect WhereDynamicFilter(DynamicFilterInfo filter) { if (filter == null) return this as TSelect; var sb = new StringBuilder(); ParseFilter(DynamicFilterLogic.And, filter, true); this.Where(sb.ToString()); sb.Clear(); return this as TSelect; void ParseFilter(DynamicFilterLogic logic, DynamicFilterInfo fi, bool isend) { if (string.IsNullOrEmpty(fi.Field) == false) { var field = fi.Field.Split('.').Select(a => a.Trim()).ToArray(); Expression exp = null; if (field.Length == 1) { foreach (var tb in _tables) { if (tb.Table.ColumnsByCs.TryGetValue(field[0], out var col) && tb.Table.Properties.TryGetValue(field[0], out var prop)) { tb.Parameter = Expression.Parameter(tb.Table.Type, tb.Alias); exp = Expression.MakeMemberAccess(tb.Parameter, prop); break; } } if (exp == null) throw new Exception($"无法匹配 {fi.Field}"); } else { var firstTb = _tables[0]; var firstTbs = _tables.Where(a => a.AliasInit == field[0]).ToArray(); if (firstTbs.Length == 1) firstTb = firstTbs[0]; firstTb.Parameter = Expression.Parameter(firstTb.Table.Type, firstTb.Alias); var currentType = firstTb.Table.Type; Expression currentExp = firstTb.Parameter; for (var x = 0; x < field.Length; x++) { var tmp1 = field[x]; if (_commonUtils.GetTableByEntity(currentType).Properties.TryGetValue(tmp1, out var prop) == false) throw new ArgumentException($"{currentType.DisplayCsharp()} 无法找到属性名 {tmp1}"); currentType = prop.PropertyType; currentExp = Expression.MakeMemberAccess(currentExp, prop); } exp = currentExp; } switch (fi.Operator) { case DynamicFilterOperator.Contains: exp = Expression.Call(exp, MethodStringContains, Expression.Constant(fi.Value)); break; case DynamicFilterOperator.StartsWith: exp = Expression.Call(exp, MethodStringStartsWith, Expression.Constant(fi.Value)); break; case DynamicFilterOperator.EndsWith: exp = Expression.Call(exp, MethodStringEndsWith, Expression.Constant(fi.Value)); break; case DynamicFilterOperator.NotContains: exp = Expression.Not(Expression.Call(exp, MethodStringContains, Expression.Constant(fi.Value))); break; case DynamicFilterOperator.NotStartsWith: exp = Expression.Not(Expression.Call(exp, MethodStringStartsWith, Expression.Constant(fi.Value))); break; case DynamicFilterOperator.NotEndsWith: exp = Expression.Not(Expression.Call(exp, MethodStringEndsWith, Expression.Constant(fi.Value))); break; case DynamicFilterOperator.Eq: case DynamicFilterOperator.Equals: case DynamicFilterOperator.Equal: exp = Expression.Equal(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value), exp.Type)); break; case DynamicFilterOperator.NotEqual: exp = Expression.NotEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value), exp.Type)); break; case DynamicFilterOperator.GreaterThan: exp = Expression.GreaterThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value), exp.Type)); break; case DynamicFilterOperator.GreaterThanOrEqual: exp = Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value), exp.Type)); break; case DynamicFilterOperator.LessThan: exp = Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value), exp.Type)); break; case DynamicFilterOperator.LessThanOrEqual: exp = Expression.LessThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fi.Value), exp.Type)); break; case DynamicFilterOperator.Range: var fiValueRangeArray = fi.Value.Split(','); if (fiValueRangeArray.Length != 2) throw new ArgumentException($"Range 对应 Value 应该逗号分割,并且长度为 2"); exp = Expression.AndAlso( Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[0]), exp.Type)), Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[1]), exp.Type))); break; case DynamicFilterOperator.Any: var fiValueAnyArray = fi.Value.Split(','); var fiValueAnyArrayType = exp.Type.MakeArrayType(); exp = Expression.Call(GetMethodEnumerableContains(exp.Type), Expression.Constant(Utils.GetDataReaderValue(fiValueAnyArrayType, fiValueAnyArray), fiValueAnyArrayType), exp); break; } var sql = _commonExpression.ExpressionWhereLambda(_tables, exp, null, null, _params); sb.Append(sql); } if (fi.Filters?.Any() == true) { if (string.IsNullOrEmpty(fi.Field) == false) sb.Append(" AND "); if (fi.Logic == DynamicFilterLogic.Or) sb.Append("("); for (var x = 0; x < fi.Filters.Count; x++) ParseFilter(fi.Logic, fi.Filters[x], x == fi.Filters.Count - 1); if (fi.Logic == DynamicFilterLogic.Or) sb.Append(")"); } if (isend == false) { if (string.IsNullOrEmpty(fi.Field) == false || fi.Filters?.Any() == true) { switch (filter.Logic) { case DynamicFilterLogic.And: sb.Append(" AND "); break; case DynamicFilterLogic.Or: sb.Append(" OR "); break; } } } } } public TSelect DisableGlobalFilter(params string[] name) { if (_whereGlobalFilter.Any() == false) return this as TSelect; if (name?.Any() != true) { _whereCascadeExpression.RemoveRange(0, _whereGlobalFilter.Count); _whereGlobalFilter.Clear(); return this as TSelect; } foreach (var n in name) { if (n == null) continue; var idx = _whereGlobalFilter.FindIndex(a => string.Compare(a.Name, n, true) == 0); if (idx == -1) continue; _whereCascadeExpression.RemoveAt(idx); _whereGlobalFilter.RemoveAt(idx); } return this as TSelect; } public TSelect ForUpdate(bool noawait = false) { if (_transaction == null && _orm.Ado.TransactionCurrentThread == null) throw new Exception("安全起见,请务必在事务开启之后,再使用 ForUpdate"); switch (_orm.Ado.DataType) { case DataType.MySql: case DataType.OdbcMySql: _tosqlAppendContent = " for update"; break; case DataType.SqlServer: case DataType.OdbcSqlServer: _aliasRule = (_, old) => $"{old} With(UpdLock, RowLock{(noawait ? ", NoWait" : "")})"; break; case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: _tosqlAppendContent = $" for update{(noawait ? " nowait" : "")}"; break; case DataType.Oracle: case DataType.OdbcOracle: _tosqlAppendContent = $" for update{(noawait ? " nowait" : "")}"; break; case DataType.Sqlite: break; case DataType.OdbcDameng: case DataType.Dameng: case DataType.OdbcKingbaseES: _tosqlAppendContent = $" for update{(noawait ? " nowait" : "")}"; break; } return this as TSelect; } #region common protected double InternalAvg(Expression exp) { var list = this.ToList($"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"); return list.Sum() / list.Count; } protected TMember InternalMax(Expression exp) => this.ToList($"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}").Max(); protected TMember InternalMin(Expression exp) => this.ToList($"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}").Min(); protected decimal InternalSum(Expression exp) => this.ToList($"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}").Sum(); public ISelectGrouping InternalGroupBy(Expression columns) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = -10000; //临时规则,不返回 as1 _commonExpression.ReadAnonymousField(_tables, field, map, ref index, columns, null, _whereCascadeExpression, false); //不走 DTO 映射 var sql = field.ToString(); this.GroupBy(sql.Length > 0 ? sql.Substring(2) : null); return new SelectGroupingProvider(_orm, this, map, sql, _commonExpression, _tables); } public TSelect InternalJoin(Expression exp, SelectTableInfoType joinType) { _commonExpression.ExpressionJoinLambda(_tables, joinType, exp, null, _whereCascadeExpression); return this as TSelect; } protected TSelect InternalJoin(Expression exp, SelectTableInfoType joinType) { var tb = _commonUtils.GetTableByEntity(typeof(T2)); if (tb == null) throw new ArgumentException("T2 类型错误"); _tables.Add(new SelectTableInfo { Table = tb, Alias = $"IJ{_tables.Count}", On = null, Type = joinType }); _commonExpression.ExpressionJoinLambda(_tables, joinType, exp, null, _whereCascadeExpression); return this as TSelect; } protected TSelect InternalOrderBy(Expression column) => this.OrderBy(_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true, null)); protected TSelect InternalOrderByDescending(Expression column) => this.OrderBy($"{_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, column, true, null)} DESC"); public List InternalToList(Expression select) => this.ToListMapReader(this.GetExpressionField(select)); protected string InternalToSql(Expression select, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) { var af = this.GetExpressionField(select, fieldAlias); return this.ToSql(af.field); } protected DataTable InternalToDataTable(Expression select) { var sql = this.InternalToSql(select, FieldAliasOptions.AsProperty); //DataTable 使用 AsProperty var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); DataTable ret = null; Exception exception = null; try { ret = _orm.Ado.ExecuteDataTable(_connection, _transaction, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } protected TReturn InternalToAggregate(Expression select) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, null, _whereCascadeExpression, false); //不走 DTO 映射 return this.ToListMapReader(new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null)).FirstOrDefault(); } public TSelect InternalWhere(Expression exp) => exp == null ? this as TSelect : this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp, null, _whereCascadeExpression, _params)); #endregion #if net40 #else async public Task AnyAsync() { this.Limit(1); return (await this.ToListAsync($"1{_commonUtils.FieldAsAlias("as1")}")).Sum() > 0; //这里的 Sum 为了分表查询 } async public Task CountAsync() { var tmpOrderBy = _orderby; _orderby = null; try { return (await this.ToListAsync($"count(1){_commonUtils.FieldAsAlias("as1")}")).Sum(); //这里的 Sum 为了分表查询 } finally { _orderby = tmpOrderBy; } } async public Task ToDataTableAsync(string field = null) { var sql = this.ToSql(field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); DataTable ret = null; Exception exception = null; try { ret = await _orm.Ado.ExecuteDataTableAsync(_connection, _transaction, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } async public Task> ToListAsync(string field) { var sql = this.ToSql(field); var type = typeof(TTuple); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); var flagStr = $"ToListField:{field}"; Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, dr, 0, _commonUtils); ret.Add((TTuple)read.Value); return Task.FromResult(false); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } async internal Task> ToListAfPrivateAsync(string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { ret.Add(af.Read(_orm, dr)); if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, dr, ref idx, false, null)); } return Task.FromResult(false); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } foreach (var include in _includeToListAsync) await include?.Invoke(ret); _trackToList?.Invoke(ret); return ret; } internal Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { string sql = null; if (otherData?.Length > 0) { var sbField = new StringBuilder().Append(af.Field); foreach (var other in otherData) sbField.Append(other.field); sql = this.ToSql(sbField.ToString()); } else sql = this.ToSql(af.Field); return ToListAfPrivateAsync(sql, af, otherData); } public Task> ToDictionaryAsync(Func keySelector) => ToDictionaryAsync(keySelector, a => a); async public Task> ToDictionaryAsync(Func keySelector, Func elementSelector) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector)); var af = this.GetAllFieldExpressionTreeLevel2(); var sql = this.ToSql(af.Field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new Dictionary(); Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { var item = af.Read(_orm, dr); ret.Add(keySelector(item), elementSelector(item)); return Task.FromResult(false); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (typeof(TElement) == typeof(T1)) _trackToList?.Invoke(ret.Values); return ret; } public virtual Task> ToListAsync(bool includeNestedMembers = false) { if (_selectExpression != null) return this.InternalToListAsync(_selectExpression); return this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } async public Task ToOneAsync() { this.Limit(1); return (await this.ToListAsync()).FirstOrDefault(); } public Task FirstAsync() => this.ToOneAsync(); async internal Task> ToListMrPrivateAsync(string sql, ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData) { var type = typeof(TReturn); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { var index = -1; ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, dr, ref index, false, null)); if (otherData != null) foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, dr, ref index, false, null)); return Task.FromResult(false); }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (typeof(TReturn) == typeof(T1)) foreach (var include in _includeToListAsync) await include?.Invoke(ret); _trackToList?.Invoke(ret); return ret; } internal Task> ToListMapReaderPrivateAsync(ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData) { string sql = null; if (otherData?.Length > 0) { var sbField = new StringBuilder().Append(af.field); foreach (var other in otherData) sbField.Append(other.field); sql = this.ToSql(sbField.ToString()); } else sql = this.ToSql(af.field); return ToListMrPrivateAsync(sql, af, otherData); } protected Task> ToListMapReaderAsync(ReadAnonymousTypeAfInfo af) => ToListMapReaderPrivateAsync(af, null); async protected Task InternalAvgAsync(Expression exp) { var list = await this.ToListAsync($"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"); return list.Sum() / list.Count; } async protected Task InternalMaxAsync(Expression exp) => (await this.ToListAsync($"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}")).Max(); async protected Task InternalMinAsync(Expression exp) => (await this.ToListAsync($"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}")).Min(); async protected Task InternalSumAsync(Expression exp) => (await this.ToListAsync($"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}")).Sum(); protected Task> InternalToListAsync(Expression select) => this.ToListMapReaderAsync(this.GetExpressionField(select)); async protected Task InternalToDataTableAsync(Expression select) { var sql = this.InternalToSql(select, FieldAliasOptions.AsProperty); //DataTable 使用 AsProperty var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); DataTable ret = null; Exception exception = null; try { ret = await _orm.Ado.ExecuteDataTableAsync(_connection, _transaction, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; throw ex; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } async protected Task InternalToAggregateAsync(Expression select) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, null, _whereCascadeExpression, false); //不走 DTO 映射 return (await this.ToListMapReaderAsync(new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null))).FirstOrDefault(); } #endif } }