using FreeSql.Internal.Model; using System; using System.Collections; 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.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { partial class Select0Provider { public DataTable ToDataTableByPropertyName(string[] properties) { if (properties?.Any() != true) throw new ArgumentException($"{CoreStrings.Properties_Cannot_Null}"); var sbfield = new StringBuilder(); for (var propIdx = 0; propIdx < properties.Length; propIdx++) { var property = properties[propIdx]; var exp = ConvertStringPropertyToExpression(property); if (exp == null) throw new Exception(CoreStrings.Property_Cannot_Find(property)); var field = _commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery); if (propIdx > 0) sbfield.Append(", "); sbfield.Append(field); //if (field != property) sbfield.Append(_commonUtils.FieldAsAlias(_commonUtils.QuoteSqlName("test").Replace("test", property))); } var sbfieldStr = sbfield.ToString(); sbfield.Clear(); return ToDataTable(sbfieldStr); } public DataTable ToDataTable(string field = null) { DataTable ret = null; if (_cancel?.Invoke() == true) return ret; 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); Exception exception = null; try { ret = _orm.Ado.ExecuteDataTable(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } public List ToList(string field) => ToListQfPrivate(this.ToSql(field), field); public List ToListQfPrivate(string sql, string field) { var ret = new List(); if (_cancel?.Invoke() == true) return ret; if (string.IsNullOrEmpty(sql)) return ret; var dbParms = _params.ToArray(); var type = typeof(TTuple); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); Exception exception = null; try { if (type.IsClass) ret = _orm.Ado.Query(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms); else { var flagStr = $"ToListField:{field}"; _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, fetch.Object, 0, _commonUtils); ret.Add((TTuple)read.Value); }, CommandType.Text, sql, _commandTimeout, dbParms); } } catch (Exception ex) { exception = ex; throw; } 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 ret = new List(); if (_cancel?.Invoke() == true) return ret; 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 retCount = 0; Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { ret.Add(af.Read(_orm, fetch.Object)); if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null, retCount, null, null)); } retCount++; }, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } 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) { ReadAnonymousTypeOtherInfo csspsod = null; if (_SameSelectPendingShareData != null) { var ods = new List(); if (otherData?.Any() == true) ods.AddRange(otherData); ods.Add(csspsod = new ReadAnonymousTypeOtherInfo($", {(_SameSelectPendingShareData.Any() && _SameSelectPendingShareData.Last() == null ? _SameSelectPendingShareData.Count - 1 : _SameSelectPendingShareData.Count)}{_commonUtils.FieldAsAlias("fsql_subsel_rowidx")}", new ReadAnonymousTypeInfo { CsType = typeof(int) }, new List())); otherData = ods.ToArray(); } 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().TrimStart(',')); } else sql = this.ToSql(af.Field); if (SameSelectPending(ref sql, csspsod)) return new List(); return ToListAfPrivate(sql, af, otherData); } #region ToChunk internal void ToListAfChunkPrivate(int chunkSize, Action>> chunkDone, string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { if (_cancel?.Invoke() == true) return; 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 FetchCallbackArgs> { Object = new List() }; var retCount = 0; Exception exception = null; var checkDoneTimes = 0; try { _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { ret.Object.Add(af.Read(_orm, fetch.Object)); if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null, ret.Object.Count - 1, null, null)); } retCount++; if (chunkSize > 0 && chunkSize == ret.Object.Count) { checkDoneTimes++; foreach (var include in _includeToList) include?.Invoke(ret.Object); _trackToList?.Invoke(ret.Object); chunkDone(ret); fetch.IsBreak = ret.IsBreak; ret.Object.Clear(); if (otherData != null) foreach (var other in otherData) other.retlist.Clear(); } }, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, retCount); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (ret.Object.Any() || checkDoneTimes == 0) { foreach (var include in _includeToList) include?.Invoke(ret.Object); _trackToList?.Invoke(ret.Object); 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().TrimStart(',')); } 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(CoreStrings.Before_Chunk_Cannot_Use_Select); this.ToListChunkPrivate(size, done, includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } internal void ToListMrChunkPrivate(int chunkSize, Action>> chunkDone, string sql, ReadAnonymousTypeAfInfo af) { if (_cancel?.Invoke() == true) return; 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 FetchCallbackArgs> { Object = new List() }; var retCount = 0; Exception exception = null; var checkDoneTimes = 0; try { _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { var index = -1; ret.Object.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null, ret.Object.Count, af.fillIncludeMany, af.fillSubSelectMany)); retCount++; if (chunkSize > 0 && chunkSize == ret.Object.Count) { checkDoneTimes++; foreach (var include in _includeToList) include?.Invoke(ret.Object); _trackToList?.Invoke(ret.Object); chunkDone(ret); fetch.IsBreak = ret.IsBreak; ret.Object.Clear(); } }, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, retCount); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } if (ret.Object.Any() || checkDoneTimes == 0) { foreach (var include in _includeToList) include?.Invoke(ret.Object); _trackToList?.Invoke(ret.Object); chunkDone(ret); } } public void InternalToChunk(Expression select, int size, Action>> done) { var af = this.GetExpressionField(select); var sql = this.ToSql(af.field); this.ToListMrChunkPrivate(size, done, sql, af); } #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 ret = new Dictionary(); if (_cancel?.Invoke() == true) return ret; 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); Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { var item = af.Read(_orm, fetch.Object); ret.Add(keySelector(item), elementSelector(item)); }, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } 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; } internal List ToListMrPrivate(string sql, ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData) { var ret = new List(); if (_cancel?.Invoke() == true) return ret; var dbParms = _params.ToArray(); var type = typeof(TReturn); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var retCount = 0; Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { var index = -1; ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null, retCount, af.fillIncludeMany, af.fillSubSelectMany)); if (otherData != null) foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref index, false, null, retCount, null, null)); retCount++; }, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } 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) { ReadAnonymousTypeOtherInfo csspsod = null; if (_SameSelectPendingShareData != null) { var ods = new List(); if (otherData?.Any() == true) ods.AddRange(otherData); ods.Add(csspsod = new ReadAnonymousTypeOtherInfo($", {(_SameSelectPendingShareData.Any() && _SameSelectPendingShareData.Last() == null ? _SameSelectPendingShareData.Count - 1 : _SameSelectPendingShareData.Count)}{_commonUtils.FieldAsAlias("fsql_subsel_rowidx")}", new ReadAnonymousTypeInfo { CsType = typeof(int) }, new List())); otherData = ods.ToArray(); } 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().TrimStart(',')); } else sql = this.ToSql(af.field); if (SameSelectPending(ref sql, csspsod)) return new List(); return ToListMrPrivate(sql, af, otherData); } protected List ToListMapReader(ReadAnonymousTypeAfInfo af) => ToListMapReaderPrivate(af, null); static ConcurrentDictionary _dicGetAllFieldExpressionTree = new ConcurrentDictionary(); public class GetAllFieldExpressionTreeInfo { public string Field { get; set; } public int FieldCount { get; set; } public Func Read { get; set; } } public GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevelAll() { return _dicGetAllFieldExpressionTree.GetOrAdd($"*{string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Table.Type.FullName}-{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(StringComparer.CurrentCultureIgnoreCase); 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); var columnSql = $"{tbi.Alias}.{quoteName}"; var rereadSql = _commonUtils.RereadColumn(col, columnSql); field.Append(rereadSql); ++index; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else { dicfield.Add(quoteName, true); if (rereadSql != columnSql) field.Append(_commonUtils.FieldAsAlias(quoteName)); } ++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.IfThen( Expression.NotEqual(retExp, Expression.Constant(null)), 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() }; }); } static EventHandler _OldAuditDataReaderHandler; public GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevel2(bool isRereadSql = true) { if (_diymemexpWithTempQuery != null) { return new GetAllFieldExpressionTreeInfo { Field = "*", Read = (orm, dr) => throw new Exception("GetAllFieldExpressionTreeInfo.Read Is Null") }; } if (_selectExpression != null) //ToSql { var af = this.GetExpressionField(_selectExpression); return new GetAllFieldExpressionTreeInfo { Field = af.field, Read = (orm, dr) => throw new Exception("GetAllFieldExpressionTreeInfo.Read Is Null") }; } if (_OldAuditDataReaderHandler != _orm.Aop.AuditDataReaderHandler) { _OldAuditDataReaderHandler = _orm.Aop.AuditDataReaderHandler; //清除单表 ExppressionTree _dicGetAllFieldExpressionTree.TryRemove($"{_orm.Ado.DataType}-{_tables[0].Table.DbName}-{_tables[0].Table.Type.FullName}-{_tables[0].Alias}-{_tables[0].Type}", out var oldet); } return _dicGetAllFieldExpressionTree.GetOrAdd(string.Join("+", _tables.Select(a => $"{_orm.Ado.DataType}-{a.Table.DbName}-{a.Table.Type.FullName}-{a.Alias}-{a.Type}-{(isRereadSql ? 1 : 0)}")), 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(StringComparer.CurrentCultureIgnoreCase); 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); var columnSql = $"{tb.Alias}.{quoteName}"; var rereadSql = isRereadSql ? _commonUtils.RereadColumn(col, columnSql) : columnSql; field.Append(rereadSql); ++index; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else { dicfield.Add(quoteName, true); if (rereadSql != columnSql) field.Append(_commonUtils.FieldAsAlias(quoteName)); } } 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); var columnSql = $"{tb2.Alias}.{quoteName}"; var rereadSql = isRereadSql ? _commonUtils.RereadColumn(col2, columnSql) : columnSql; field.Append(rereadSql); ++index; ++otherindex; if (dicfield.ContainsKey(quoteName)) field.Append(_commonUtils.FieldAsAlias($"as{index}")); else { dicfield.Add(quoteName, true); if (rereadSql != columnSql) field.Append(_commonUtils.FieldAsAlias(quoteName)); } } } //只读到二级属性 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 (tb1.TypeLazy != null) blockExp.Add( Expression.IfThen( Expression.NotEqual(readExpValue, Expression.Constant(null)), Expression.Call(retExp, tb1.TypeLazySetOrm, ormExp) ) ); //将 orm 传递给 lazy if (otherindex == 0) { //不读导航属性,优化单表读取性能 blockExp.Clear(); blockExp.AddRange(new Expression[]{ Expression.Assign(retExp, type.InternalNewExpression()), Expression.Assign(dataIndexExp, Expression.Constant(0)) }); var colidx = 0; foreach (var col in tb.Table.Columns.Values) { var drvalType = col.Attribute.MapType.NullableTypeOrThis(); var propGetSetMethod = tb.Table.Properties[col.CsName].GetSetMethod(true); if (col.CsType == col.Attribute.MapType && _orm.Aop.AuditDataReaderHandler == null && _dicMethodDataReaderGetValue.TryGetValue(col.Attribute.MapType.NullableTypeOrThis(), out var drGetValueMethod)) { Expression drvalExp = Expression.Call(rowExp, drGetValueMethod, Expression.Constant(colidx)); if (col.CsType.IsNullableType()) drvalExp = Expression.Convert(drvalExp, col.CsType); drvalExp = Expression.Condition(Expression.Call(rowExp, _MethodDataReaderIsDBNull, Expression.Constant(colidx)), Expression.Default(col.CsType), drvalExp); if (drvalType.IsArray || drvalType.IsEnum || Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(drvalType)) { var drvalExpCatch = Utils.GetDataReaderValueBlockExpression( col.CsType, Expression.Call(Utils.MethodDataReaderGetValue, new Expression[] { Expression.Constant(_commonUtils), rowExp, Expression.Constant(colidx) }) ); blockExp.Add(Expression.TryCatch( Expression.Call(retExp, propGetSetMethod, drvalExp), Expression.Catch(typeof(Exception), Expression.Call(retExp, propGetSetMethod, Expression.Convert(drvalExpCatch, col.CsType)) //Expression.Throw(Expression.Constant(new Exception($"{_commonUtils.QuoteSqlName(col.Attribute.Name)} is NULL,除非设置特性 [Column(IsNullable = false)]"))) ))); } else { blockExp.Add(Expression.TryCatch( Expression.Call(retExp, propGetSetMethod, drvalExp), Expression.Catch(typeof(Exception), Expression.Call(retExp, propGetSetMethod, Expression.Default(col.CsType)) //Expression.Throw(Expression.Constant(new Exception($"{_commonUtils.QuoteSqlName(col.Attribute.Name)} is NULL,除非设置特性 [Column(IsNullable = false)]"))) ))); } } else { if (drvalType.IsArray || drvalType.IsEnum || Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(drvalType)) { var drvalExp = Utils.GetDataReaderValueBlockExpression( col.CsType, Expression.Call(Utils.MethodDataReaderGetValue, new Expression[] { Expression.Constant(_commonUtils), rowExp, Expression.Constant(colidx) }) ); blockExp.Add(Expression.Call(retExp, propGetSetMethod, Expression.Convert(drvalExp, col.CsType))); } } colidx++; } if (tb1.TypeLazy != null) blockExp.Add(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(), FieldCount = index, Read = Expression.Lambda>(Expression.Block(new[] { retExp, dataIndexExp, readExp }, blockExp), new[] { ormExp, rowExp }).Compile() }; }); } protected double InternalAvg(Expression exp) { var field = $"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) { var list = this.ToList(field); return list.Sum() / list.Count; } var sql = GetNestSelectSql(exp, field, ToSql); var list2 = ToListQfPrivate(sql, field); return list2.Sum() / list2.Count; } protected TMember InternalMax(Expression exp) { var field = $"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) return this.ToList(field).Max(); var sql = GetNestSelectSql(exp, field, ToSql); return ToListQfPrivate(sql, field).Max(); } protected TMember InternalMin(Expression exp) { var field = $"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) return this.ToList(field).Min(); var sql = GetNestSelectSql(exp, field, ToSql); return ToListQfPrivate(sql, field).Min(); } protected decimal InternalSum(Expression exp) { var field = $"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) return this.ToList(field).Sum(); var sql = GetNestSelectSql(exp, field, ToSql); return ToListQfPrivate(sql, field).Sum(); } public ISelectGrouping InternalGroupBy(Expression columns) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = CommonExpression.ReadAnonymousFieldAsCsNameGroupBy; //临时规则,不返回 as1 _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, columns, null, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, false); //不走 DTO 映射,不处理 IncludeMany 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, _tableRule, joinType, exp, _diymemexpWithTempQuery, _whereGlobalFilter); return this as TSelect; } protected TSelect InternalJoin(Expression exp, SelectTableInfoType joinType) { var tb = _commonUtils.GetTableByEntity(typeof(T2)); if (tb == null) throw new ArgumentException(CoreStrings.T2_Type_Error); _tables.Add(new SelectTableInfo { Table = tb, Alias = $"IJ{_tables.Count}", On = null, Type = joinType }); _commonExpression.ExpressionJoinLambda(_tables, _tableRule, joinType, exp, _diymemexpWithTempQuery, _whereGlobalFilter); return this as TSelect; } protected TSelect InternalOrderBy(Expression column) { if (column.NodeType == ExpressionType.Lambda) column = (column as LambdaExpression)?.Body; switch (column?.NodeType) { case ExpressionType.New: var newExp = column as NewExpression; if (newExp == null) break; for (var a = 0; a < newExp.Members.Count; a++) this.OrderBy(_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, newExp.Arguments[a], true, _diymemexpWithTempQuery)); return this as TSelect; } return this.OrderBy(_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, column, true, _diymemexpWithTempQuery)); } protected TSelect InternalOrderByDescending(Expression column) { if (column.NodeType == ExpressionType.Lambda) column = (column as LambdaExpression)?.Body; switch (column?.NodeType) { case ExpressionType.New: var newExp = column as NewExpression; if (newExp == null) break; for (var a = 0; a < newExp.Members.Count; a++) this.OrderBy($"{_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, newExp.Arguments[a], true, _diymemexpWithTempQuery)} DESC"); return this as TSelect; } return this.OrderBy($"{_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, column, true, _diymemexpWithTempQuery)} DESC"); } public List InternalToList(Expression select) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; var findSubSelectMany = new List(); _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, findSubSelectMany, true); var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); if (findSubSelectMany.Any() == false) return this.ToListMapReaderPrivate(af, new ReadAnonymousTypeOtherInfo[0]); af.fillSubSelectMany = new List>(); //查询 SubSelectMany var otherAfmanys = findSubSelectMany.Select(a => { var vst = new FindAllMemberExpressionVisitor(this); vst.Visit(a); var finds = vst.Result; var afs = new List>(); foreach (var find in finds) { var otherMap = new ReadAnonymousTypeInfo(); field.Clear(); _commonExpression.ReadAnonymousField(_tables, _tableRule, field, otherMap, ref index, find.Item1, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, true); var otherRet = new List(); var otherAf = new ReadAnonymousTypeOtherInfo(field.ToString(), otherMap, otherRet); afs.Add(NativeTuple.Create(find.Item1, find.Item2, otherAf)); } return afs; }).ToList(); var otherAfdic = otherAfmanys.SelectMany(a => a).GroupBy(a => a.Item1.ToString()).ToDictionary(a => a.Key, a => a.ToList()); var otherAfs = otherAfdic.Select(a => a.Value.First().Item3).ToArray(); var ret = this.ToListMapReaderPrivate(af, otherAfs); if (ret.Any() == false || otherAfmanys.Any() == false) return ret; var rmev = new ReplaceMemberExpressionVisitor(); for (var a = 0; a < otherAfmanys.Count; a++) { if (otherAfmanys[a].Any() == false) { var otherList = Expression.Lambda(findSubSelectMany[a]).Compile().DynamicInvoke() as IEnumerable; foreach (var otherListItem in otherList) for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) af.fillSubSelectMany[b].Item2.Add(otherListItem); continue; } var sspShareData = new List>(); try { var newexp = findSubSelectMany[a]; var newexpParms = otherAfmanys[a].Select(d => { var newexpParm = Expression.Parameter(d.Item1.Type); newexp = rmev.Replace(newexp, d.Item1, newexpParm); return newexpParm; }).ToArray(); newexp = SetSameSelectPendingShareDataWithExpression(newexp, sspShareData); var newexpFunc = Expression.Lambda(newexp, newexpParms).Compile(); var newexpParamVals = otherAfmanys[a].Select(d => otherAfdic[d.Item1.ToString()].First().Item3.retlist).ToArray(); for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) { var vals = newexpParamVals.Select(d => d[c]).ToArray(); if (c == ret.Count - 1) sspShareData.Add(null); //flush flag var diret = newexpFunc.DynamicInvoke(vals); if (c < ret.Count - 1) continue; var otherList = diret as IEnumerable; var retlistidx = 0; foreach (var otherListItem in otherList) { var retlist = sspShareData[0].Item3.retlist; while (retlistidx >= retlist.Count) { sspShareData.RemoveAt(0); retlist = sspShareData[0].Item3.retlist; retlistidx = 0; } int.TryParse(retlist[retlistidx++]?.ToString(), out var tryrowidx); af.fillSubSelectMany[tryrowidx * otherAfmanys.Count + a].Item2.Add(otherListItem); } } } finally { sspShareData.Clear(); } } return ret; } protected string InternalToSql(Expression select, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) { var af = this.GetExpressionField(select, fieldAlias); return this.ToSql(af.field); } protected string InternalGetInsertIntoToSql(string tableName, Expression select) { var tb = _orm.CodeFirst.GetTableByEntity(typeof(TTargetEntity)); if (tb == null) throw new ArgumentException(CoreStrings.InsertInto_TypeError(typeof(TTargetEntity).DisplayCsharp())); if (string.IsNullOrEmpty(tableName)) tableName = tb.DbName; if (_orm.CodeFirst.IsSyncStructureToLower) tableName = tableName.ToLower(); if (_orm.CodeFirst.IsSyncStructureToUpper) tableName = tableName.ToUpper(); if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(typeof(TTargetEntity), tableName); var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = -10000; //临时规则,不返回 as1 _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select, null, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, false); //不走 DTO 映射,不处理 IncludeMany var childs = map.Childs; if (childs.Any() == false) throw new ArgumentException(CoreStrings.InsertInto_No_Property_Selected(typeof(TTargetEntity).DisplayCsharp())); foreach (var col in tb.Columns.Values) { if (col.Attribute.IsIdentity && string.IsNullOrEmpty(col.DbInsertValue)) continue; if (col.Attribute.CanInsert == false) continue; if (childs.Any(a => a.CsName == col.CsName)) continue; var dbfield = string.IsNullOrWhiteSpace(col.DbInsertValue) == false ? col.DbInsertValue : col.DbDefaultValue; childs.Add(new ReadAnonymousTypeInfo { DbField = dbfield, CsName = col.CsName }); } var selectField = string.Join(", ", childs.Select(a => a.DbField)); var selectSql = this.ToSql(selectField); var insertField = string.Join(", ", childs.Select(a => _commonUtils.QuoteSqlName(tb.ColumnsByCs[a.CsName].Attribute.Name))); var sql = $"INSERT INTO {_commonUtils.QuoteSqlName(tableName)}({insertField})\r\n{selectSql}"; return sql; } public int InternalInsertInto(string tableName, Expression select) { int ret = 0; if (_cancel?.Invoke() == true) return ret; var sql = this.InternalGetInsertIntoToSql(tableName, select); var dbParms = _params.ToArray(); var tb = _orm.CodeFirst.GetTableByEntity(typeof(TTargetEntity)); var before = new Aop.CurdBeforeEventArgs(tb.Type, tb, Aop.CurdType.Insert, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); Exception exception = null; try { ret = _orm.Ado.ExecuteNonQuery(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } protected DataTable InternalToDataTable(Expression select) { DataTable ret = null; if (_cancel?.Invoke() == true) return ret; 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); Exception exception = null; try { ret = _orm.Ado.ExecuteDataTable(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } protected TReturn InternalToAggregate(Expression select) { var tmpOrderBy = _orderby; _orderby = null; //解决 select count(1) from t order by id 这样的 SQL 错误 try { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select, null, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, false); //不走 DTO 映射,不处理 IncludeMany var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); if (GetTableRuleUnions().Count <= 1) return this.ToListMapReader(af).FirstOrDefault(); var sql = GetNestSelectSql(select, af.field, ToSql); return ToListMrPrivate(sql, af, null).FirstOrDefault(); } finally { _orderby = tmpOrderBy; } } public TSelect InternalWhere(Expression exp) => exp == null ? this as TSelect : this.Where(_commonExpression.ExpressionWhereLambda(_tables, _tableRule, exp, _diymemexpWithTempQuery, _whereGlobalFilter, _params)); #region Async #if net40 #else public Task ToDataTableByPropertyNameAsync(string[] properties, CancellationToken cancellationToken) { if (properties?.Any() != true) throw new ArgumentException($"{CoreStrings.Properties_Cannot_Null}"); var sbfield = new StringBuilder(); for (var propIdx = 0; propIdx < properties.Length; propIdx++) { var property = properties[propIdx]; var exp = ConvertStringPropertyToExpression(property); if (exp == null) throw new Exception(CoreStrings.Property_Cannot_Find(property)); var field = _commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery); if (propIdx > 0) sbfield.Append(", "); sbfield.Append(field); //if (field != property) sbfield.Append(_commonUtils.FieldAsAlias(_commonUtils.QuoteSqlName("test").Replace("test", property))); } var sbfieldStr = sbfield.ToString(); sbfield.Clear(); return ToDataTableAsync(sbfieldStr, cancellationToken); } async public Task ToDataTableAsync(string field, CancellationToken cancellationToken) { DataTable ret = null; if (_cancel?.Invoke() == true) return ret; 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); Exception exception = null; try { ret = await _orm.Ado.ExecuteDataTableAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } public Task> ToListAsync(string field, CancellationToken cancellationToken) => ToListQfPrivateAsync(this.ToSql(field), field, cancellationToken); async public Task> ToListQfPrivateAsync(string sql, string field, CancellationToken cancellationToken) { var ret = new List(); if (_cancel?.Invoke() == true) return ret; if (string.IsNullOrEmpty(sql)) return ret; var dbParms = _params.ToArray(); var type = typeof(TTuple); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); Exception exception = null; try { if (type.IsClass) ret = await _orm.Ado.QueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); else { var flagStr = $"ToListField:{field}"; await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, fetch => { var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, fetch.Object, 0, _commonUtils); ret.Add((TTuple)read.Value); return Task.FromResult(false); }, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } } catch (Exception ex) { exception = ex; throw; } 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, CancellationToken cancellationToken) { var ret = new List(); if (_cancel?.Invoke() == true) return ret; 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 retCount = 0; Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, fetch => { ret.Add(af.Read(_orm, fetch.Object)); if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null, retCount, null, null)); } retCount++; return Task.FromResult(false); }, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } foreach (var include in _includeToListAsync) await include?.Invoke(ret, cancellationToken); _trackToList?.Invoke(ret); return ret; } internal Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { ReadAnonymousTypeOtherInfo csspsod = null; if (_SameSelectPendingShareData != null) { var ods = new List(); if (otherData?.Any() == true) ods.AddRange(otherData); ods.Add(csspsod = new ReadAnonymousTypeOtherInfo($", {(_SameSelectPendingShareData.Any() && _SameSelectPendingShareData.Last() == null ? _SameSelectPendingShareData.Count - 1 : _SameSelectPendingShareData.Count)}{_commonUtils.FieldAsAlias("fsql_subsel_rowidx")}", new ReadAnonymousTypeInfo { CsType = typeof(int) }, new List())); otherData = ods.ToArray(); } 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().TrimStart(',')); } else sql = this.ToSql(af.Field); if (SameSelectPending(ref sql, csspsod)) return Task.FromResult(new List()); return ToListAfPrivateAsync(sql, af, otherData, cancellationToken); } public Task> ToDictionaryAsync(Func keySelector, CancellationToken cancellationToken) => ToDictionaryAsync(keySelector, a => a, cancellationToken); async public Task> ToDictionaryAsync(Func keySelector, Func elementSelector, CancellationToken cancellationToken) { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector)); var ret = new Dictionary(); if (_cancel?.Invoke() == true) return ret; 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); Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, fetch => { var item = af.Read(_orm, fetch.Object); ret.Add(keySelector(item), elementSelector(item)); return Task.FromResult(false); }, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } catch (Exception ex) { exception = ex; throw; } 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; } async internal Task> ToListMrPrivateAsync(string sql, ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { var ret = new List(); if (_cancel?.Invoke() == true) return ret; var dbParms = _params.ToArray(); var type = typeof(TReturn); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var retCount = 0; Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, fetch => { var index = -1; ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null, retCount, af.fillIncludeMany, af.fillSubSelectMany)); if (otherData != null) foreach (var other in otherData) other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref index, false, null, retCount, null, null)); retCount++; return Task.FromResult(false); }, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } catch (Exception ex) { exception = ex; throw; } 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, cancellationToken); _trackToList?.Invoke(ret); return ret; } internal Task> ToListMapReaderPrivateAsync(ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { ReadAnonymousTypeOtherInfo csspsod = null; if (_SameSelectPendingShareData != null) { var ods = new List(); if (otherData?.Any() == true) ods.AddRange(otherData); ods.Add(csspsod = new ReadAnonymousTypeOtherInfo($", {(_SameSelectPendingShareData.Any() && _SameSelectPendingShareData.Last() == null ? _SameSelectPendingShareData.Count - 1 : _SameSelectPendingShareData.Count)}{_commonUtils.FieldAsAlias("fsql_subsel_rowidx")}", new ReadAnonymousTypeInfo { CsType = typeof(int) }, new List())); otherData = ods.ToArray(); } 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().TrimStart(',')); } else sql = this.ToSql(af.field); if (SameSelectPending(ref sql, csspsod)) return Task.FromResult(new List()); return ToListMrPrivateAsync(sql, af, otherData, cancellationToken); } protected Task> ToListMapReaderAsync(ReadAnonymousTypeAfInfo af, CancellationToken cancellationToken) => ToListMapReaderPrivateAsync(af, null, cancellationToken); async protected Task InternalAvgAsync(Expression exp, CancellationToken cancellationToken) { var field = $"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) { var list = await this.ToListAsync(field, cancellationToken); return list.Sum() / list.Count; } var sql = GetNestSelectSql(exp, field, ToSql); var list2 = await ToListQfPrivateAsync(sql, field, cancellationToken); return list2.Sum() / list2.Count; } async protected Task InternalMaxAsync(Expression exp, CancellationToken cancellationToken) { var field = $"max({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) return (await this.ToListAsync(field, cancellationToken)).Max(); var sql = GetNestSelectSql(exp, field, ToSql); return (await ToListQfPrivateAsync(sql, field, cancellationToken)).Max(); } async protected Task InternalMinAsync(Expression exp, CancellationToken cancellationToken) { var field = $"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) return (await this.ToListAsync(field, cancellationToken)).Min(); var sql = GetNestSelectSql(exp, field, ToSql); return (await ToListQfPrivateAsync(sql, field, cancellationToken)).Min(); } async protected Task InternalSumAsync(Expression exp, CancellationToken cancellationToken) { var field = $"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, _tableRule, null, SelectTableInfoType.From, exp, true, _diymemexpWithTempQuery)}){_commonUtils.FieldAsAlias("as1")}"; if (_limit <= 0 && _skip <= 0) return (await this.ToListAsync(field, cancellationToken)).Sum(); var sql = GetNestSelectSql(exp, field, ToSql); return (await ToListQfPrivateAsync(sql, field, cancellationToken)).Sum(); } static ConcurrentDictionary _dicGetMethodsByName = new ConcurrentDictionary(); async protected Task> InternalToListAsync(Expression select, CancellationToken cancellationToken) { //【注意】:此异步有特别逻辑,因为要处理子查询集合 ToList -> ToListAsync,原因是 LambdaExpression 表达式树内不支持 await Async var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; var findSubSelectMany = new List(); _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, findSubSelectMany, true); var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); if (findSubSelectMany.Any() == false) return await this.ToListMapReaderPrivateAsync(af, new ReadAnonymousTypeOtherInfo[0], cancellationToken); af.fillSubSelectMany = new List>(); //查询 SubSelectMany var otherAfmanys = findSubSelectMany.Select(a => { var vst = new FindAllMemberExpressionVisitor(this); vst.Visit(a); var finds = vst.Result; var afs = new List>(); foreach (var find in finds) { var otherMap = new ReadAnonymousTypeInfo(); field.Clear(); _commonExpression.ReadAnonymousField(_tables, _tableRule, field, otherMap, ref index, find.Item1, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, true); var otherRet = new List(); var otherAf = new ReadAnonymousTypeOtherInfo(field.ToString(), otherMap, otherRet); afs.Add(NativeTuple.Create(find.Item1, find.Item2, otherAf)); } return afs; }).ToList(); var otherAfdic = otherAfmanys.SelectMany(a => a).GroupBy(a => a.Item1.ToString()).ToDictionary(a => a.Key, a => a.ToList()); var otherAfs = otherAfdic.Select(a => a.Value.First().Item3).ToArray(); var ret = await this.ToListMapReaderPrivateAsync(af, otherAfs, cancellationToken); if (ret.Any() == false || otherAfmanys.Any() == false) return ret; var rmev = new ReplaceMemberExpressionVisitor(); for (var a = 0; a < otherAfmanys.Count; a++) { if (otherAfmanys[a].Any() == false) { var otherList = Expression.Lambda(findSubSelectMany[a]).Compile().DynamicInvoke() as IEnumerable; foreach (var otherListItem in otherList) for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) af.fillSubSelectMany[b].Item2.Add(otherListItem); continue; } var sspShareData = new List>(); try { var newexp = findSubSelectMany[a]; var newexpParms = otherAfmanys[a].Select(d => { var newexpParm = Expression.Parameter(d.Item1.Type); newexp = rmev.Replace(newexp, d.Item1, newexpParm); return newexpParm; }).ToArray(); var newexpCallExp = (newexp as MethodCallExpression); if (newexpCallExp?.Object != null) { var asyncMethods = _dicGetMethodsByName.GetOrAdd(newexpCallExp.Object.Type, dgmbn => dgmbn.GetMethods().Where(c => c.Name == $"{newexpCallExp.Method.Name}Async") .Concat(dgmbn.GetInterfaces().SelectMany(b => b.GetMethods().Where(c => c.Name == $"{newexpCallExp.Method.Name}Async"))).ToArray()); var asyncMethod = asyncMethods.Length == 1 ? asyncMethods.First() : null; var newexpMethodGenericArgs = newexpCallExp.Method.GetGenericArguments(); var newexpMethodParmArgs = newexpCallExp.Method.GetParameters(); if (asyncMethods.Length > 1) { asyncMethods = asyncMethods .Where(b => { var bGenericArgs = b.GetGenericArguments(); return bGenericArgs.Length == newexpMethodGenericArgs.Length; }) .Select(b => newexpMethodGenericArgs.Length == 0 ? b : b.MakeGenericMethod(newexpMethodGenericArgs)) .Where(b => { var bParmArgs = b.GetParameters(); return bParmArgs.Length - 1 == newexpMethodParmArgs.Length && newexpMethodParmArgs.Where((c, d) => c.ParameterType == bParmArgs[d].ParameterType).Count() == newexpMethodParmArgs.Length; }).ToArray(); if (asyncMethods.Length == 1) asyncMethod = asyncMethods.First(); } if (asyncMethod != null) newexp = Expression.Call(newexpCallExp.Object, asyncMethod, newexpCallExp.Arguments.Concat(new[] { Expression.Constant(cancellationToken, typeof(CancellationToken)) }).ToArray()); } newexp = SetSameSelectPendingShareDataWithExpression(newexp, sspShareData); var newexpFunc = Expression.Lambda(newexp, newexpParms).Compile(); var newexpParamVals = otherAfmanys[a].Select(d => otherAfdic[d.Item1.ToString()].First().Item3.retlist).ToArray(); for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) { var vals = newexpParamVals.Select(d => d[c]).ToArray(); if (c == ret.Count - 1) sspShareData.Add(null); //flush flag var diretTask = newexpFunc.DynamicInvoke(vals) as Task; if (c < ret.Count - 1) continue; await diretTask; var diret = diretTask.GetTaskReflectionResult(); var otherList = diret as IEnumerable; var retlistidx = 0; foreach (var otherListItem in otherList) { var retlist = sspShareData[0].Item3.retlist; while (retlistidx >= retlist.Count) { sspShareData.RemoveAt(0); retlist = sspShareData[0].Item3.retlist; retlistidx = 0; } int.TryParse(retlist[retlistidx++]?.ToString(), out var tryrowidx); af.fillSubSelectMany[tryrowidx * otherAfmanys.Count + a].Item2.Add(otherListItem); } } } finally { sspShareData.Clear(); } } return ret; } async public Task InternalInsertIntoAsync(string tableName, Expression select, CancellationToken cancellationToken) { int ret = 0; if (_cancel?.Invoke() == true) return ret; var sql = this.InternalGetInsertIntoToSql(tableName, select); var dbParms = _params.ToArray(); var tb = _orm.CodeFirst.GetTableByEntity(typeof(TTargetEntity)); var before = new Aop.CurdBeforeEventArgs(tb.Type, tb, Aop.CurdType.Insert, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); Exception exception = null; try { ret = await _orm.Ado.ExecuteNonQueryAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } async protected Task InternalToDataTableAsync(Expression select, CancellationToken cancellationToken) { DataTable ret = null; if (_cancel?.Invoke() == true) return ret; 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); Exception exception = null; try { ret = await _orm.Ado.ExecuteDataTableAsync(_connection, _transaction, CommandType.Text, sql, _commandTimeout, dbParms, cancellationToken); } catch (Exception ex) { exception = ex; throw; } finally { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfterHandler?.Invoke(this, after); } return ret; } async protected Task InternalToAggregateAsync(Expression select, CancellationToken cancellationToken) { var tmpOrderBy = _orderby; _orderby = null; //解决 select count(1) from t order by id 这样的 SQL 错误 try { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, select, null, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, false); //不走 DTO 映射,不处理 IncludeMany var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); if (GetTableRuleUnions().Count <= 1) return (await this.ToListMapReaderAsync(af, cancellationToken)).FirstOrDefault(); var sql = GetNestSelectSql(select, af.field, ToSql); return (await ToListMrPrivateAsync(sql, af, null, cancellationToken)).FirstOrDefault(); } finally { _orderby = tmpOrderBy; } } #endif #endregion } }