From 7dd8eacce327dba556946bd4b5fe32a22aa3eb8a Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Mon, 17 Aug 2020 08:58:34 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=AE=8C=E5=96=84=20ToChunk=20=E5=88=86?= =?UTF-8?q?=E5=9D=97=E5=8A=A0=E8=BD=BD=E6=9F=A5=E8=AF=A2=EF=BC=8C=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=88=B0=20ISelect`1..10=20=E4=B8=AD=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Examples/orm_vs/Program.cs | 6 + FreeSql.DbContext/FreeSql.DbContext.xml | 14 +- FreeSql/FreeSql.xml | 9 + FreeSql/Interface/Curd/ISelect/ISelect1.cs | 11 +- FreeSql/Interface/Curd/ISelect/ISelect10.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect2.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect3.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect4.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect5.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect6.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect7.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect8.cs | 1 + FreeSql/Interface/Curd/ISelect/ISelect9.cs | 1 + .../SelectProvider/Select0Provider.cs | 986 +----------------- .../SelectProvider/Select0ProviderReader.cs | 962 +++++++++++++++++ .../SelectProvider/Select10Provider.cs | 6 + .../SelectProvider/Select1Provider.cs | 7 +- .../SelectProvider/Select2Provider.cs | 7 +- .../SelectProvider/Select3Provider.cs | 7 +- .../SelectProvider/Select4Provider.cs | 7 +- .../SelectProvider/Select5Provider.cs | 7 +- .../SelectProvider/Select6Provider.cs | 7 +- .../SelectProvider/Select7Provider.cs | 7 +- .../SelectProvider/Select8Provider.cs | 8 +- .../SelectProvider/Select9Provider.cs | 6 + 25 files changed, 1084 insertions(+), 982 deletions(-) create mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs diff --git a/Examples/orm_vs/Program.cs b/Examples/orm_vs/Program.cs index 929ebbf7..b27b8fad 100644 --- a/Examples/orm_vs/Program.cs +++ b/Examples/orm_vs/Program.cs @@ -370,6 +370,12 @@ namespace orm_vs testlist2.AddRange(fetch.Object); }); + var testlist22 = new List(); + fsql.Select().LeftJoin((a, b) => a.id == b.song_id).ToChunk((a, b) => new { a.title, a.create_time, b.tag_id }, 2, fetch => + { + testlist22.AddRange(fetch.Object); + }); + //sugar.Aop.OnLogExecuted = (s, e) => //{ // Trace.WriteLine(s); diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index c46aa1e3..68241282 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -130,13 +130,6 @@ 清空状态数据 - - - 根据 lambda 条件删除数据 - - - - 添加 @@ -421,7 +414,7 @@ 事务传播方式 - + 如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。 @@ -451,6 +444,11 @@ 以嵌套事务方式执行。 + + + 错误的命名,请使用 Required,在 2.0.0 删除 + + EFCore 95% 相似的 FluentApi 扩展方法 diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 07cb2cc2..6134627d 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1645,6 +1645,15 @@ + + + 执行SQL查询,分块返回数据,可减少内存开销。比如读取10万条数据,每次返回100条处理。 + + 返回类型 + 选择列 + 数据块的大小 + 处理数据块 + 执行SQL查询,返回指定字段的记录的第一条记录,记录不存在时返回 TReturn 默认值 diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index 1d44bfe6..08ccda4b 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -1,4 +1,5 @@ -using System; +using FreeSql.Internal.Model; +using System; using System.Collections.Generic; using System.Data; using System.Linq; @@ -56,6 +57,14 @@ namespace FreeSql /// /// List ToList(); + /// + /// 执行SQL查询,分块返回数据,可减少内存开销。比如读取10万条数据,每次返回100条处理。 + /// + /// 返回类型 + /// 选择列 + /// 数据块的大小 + /// 处理数据块 + void ToChunk(Expression> select, int size, Action>> done); /// /// 执行SQL查询,返回指定字段的记录的第一条记录,记录不存在时返回 TReturn 默认值 diff --git a/FreeSql/Interface/Curd/ISelect/ISelect10.cs b/FreeSql/Interface/Curd/ISelect/ISelect10.cs index e4cdafd6..2d1107e4 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect10.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect10.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect2.cs b/FreeSql/Interface/Curd/ISelect/ISelect2.cs index bd9692e5..b7890fec 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect2.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect2.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect3.cs b/FreeSql/Interface/Curd/ISelect/ISelect3.cs index 00836d0a..413bbe72 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect3.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect3.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect4.cs b/FreeSql/Interface/Curd/ISelect/ISelect4.cs index 8890624b..cfd3b6ac 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect4.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect4.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect5.cs b/FreeSql/Interface/Curd/ISelect/ISelect5.cs index f8d6cd7e..b26c016d 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect5.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect5.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect6.cs b/FreeSql/Interface/Curd/ISelect/ISelect6.cs index 63db674e..e3c46a93 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect6.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect6.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect7.cs b/FreeSql/Interface/Curd/ISelect/ISelect7.cs index fc99ef10..23fd0fbc 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect7.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect7.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect8.cs b/FreeSql/Interface/Curd/ISelect/ISelect8.cs index 836ee3fb..22e2b933 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect8.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect8.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Interface/Curd/ISelect/ISelect9.cs b/FreeSql/Interface/Curd/ISelect/ISelect9.cs index 8f44436d..87463c8e 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect9.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect9.cs @@ -32,6 +32,7 @@ namespace FreeSql DataTable ToDataTable(Expression> select); List ToList(Expression> select); List ToList(); + void ToChunk(Expression> select, int size, Action>> done); TReturn ToOne(Expression> select); TReturn First(Expression> select); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index a5166fc2..153655e0 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -4,9 +4,7 @@ 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; @@ -17,7 +15,6 @@ using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { - public abstract partial class Select0Provider { public int _limit, _skip; @@ -157,32 +154,6 @@ namespace FreeSql.Internal.CommonProvider 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; @@ -304,627 +275,6 @@ namespace FreeSql.Internal.CommonProvider 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, fetch => - { - var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, fetch.Object, 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, 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)); - } - }, 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 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)); - retCount++; - 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)); - } - 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, 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.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()); - } - 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, fetch => - { - var item = af.Read(_orm, fetch.Object); - 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, fetch => - { - var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); - if (otherData != null) - foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, 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; } - } - public 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() - }; - }); - } - public 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; @@ -1284,88 +634,41 @@ namespace FreeSql.Internal.CommonProvider } return this as TSelect; } - #region common - protected double InternalAvg(Expression exp) + public bool Any() { - var list = this.ToList($"avg({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}"); - return list.Sum() / list.Count; + this.Limit(1); + return this.ToList($"{1}{_commonUtils.FieldAsAlias("as1")}").Sum() > 0; //这里的 Sum 为了分表查询 } - 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) + public long Count() { - 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; + var tmpOrderBy = _orderby; + _orderby = null; //解决 select count(1) from t order by id 这样的 SQL 错误 try { - ret = _orm.Ado.ExecuteDataTable(_connection, _transaction, CommandType.Text, sql, dbParms); - } - catch (Exception ex) - { - exception = ex; - throw ex; + return this.ToList($"count(1){_commonUtils.FieldAsAlias("as1")}").Sum(); //这里的 Sum 为了分表查询 } finally { - var after = new Aop.CurdAfterEventArgs(before, exception, ret); - _orm.Aop.CurdAfterHandler?.Invoke(this, after); + _orderby = tmpOrderBy; } - return ret; } - - protected TReturn InternalToAggregate(Expression select) + public TSelect Count(out long count) { - 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(); + count = this.Count(); + return this as TSelect; } - - public TSelect InternalWhere(Expression exp) => exp == null ? this as TSelect : this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp, null, _whereCascadeExpression, _params)); - #endregion + 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(); #if net40 #else @@ -1374,7 +677,6 @@ namespace FreeSql.Internal.CommonProvider this.Limit(1); return (await this.ToListAsync($"1{_commonUtils.FieldAsAlias("as1")}")).Sum() > 0; //这里的 Sum 为了分表查询 } - async public Task CountAsync() { var tmpOrderBy = _orderby; @@ -1388,261 +690,17 @@ namespace FreeSql.Internal.CommonProvider _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, fetch => - { - var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, fetch.Object, 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, 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)); - } - 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, fetch => - { - var item = af.Read(_orm, fetch.Object); - 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, fetch => - { - var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); - if (otherData != null) - foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, 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 } } \ No newline at end of file diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs new file mode 100644 index 00000000..113c8554 --- /dev/null +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs @@ -0,0 +1,962 @@ +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 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, fetch => + { + var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, fetch.Object, 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, 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)); + } + }, 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 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)); + retCount++; + 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)); + } + 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, 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.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()); + } + 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); + } + + internal void ToListMrChunkPrivate(int chunkSize, Action>> chunkDone, string sql, ReadAnonymousTypeAfInfo af) + { + 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)); + 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, 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.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 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, fetch => + { + var item = af.Read(_orm, fetch.Object); + 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; + } + + 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, fetch => + { + var index = -1; + ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); + if (otherData != null) + foreach (var other in otherData) + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, 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; } + } + public 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() + }; + }); + } + public 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 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)); + + #region Async +#if net40 +#else + 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, fetch => + { + var read = Utils.ExecuteArrayRowReadClassOrTuple(flagStr, type, null, fetch.Object, 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, 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)); + } + 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, fetch => + { + var item = af.Read(_orm, fetch.Object); + 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; + } + + 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, fetch => + { + var index = -1; + ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); + if (otherData != null) + foreach (var other in otherData) + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, 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 + #endregion + } +} \ No newline at end of file diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select10Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select10Provider.cs index 8f9a4398..cfbdf04e 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select10Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select10Provider.cs @@ -134,6 +134,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T9), "i"), Expression.Parameter(typeof(T10), "j")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index f4a18423..c8b7474c 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -174,7 +174,6 @@ namespace FreeSql.Internal.CommonProvider _tables[0].Parameter = select.Parameters[0]; return this.InternalToList(select.Body); } - public List ToList() => ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -182,6 +181,12 @@ namespace FreeSql.Internal.CommonProvider typeof(TDto).InternalNewExpression(), _tables[0].Parameter ?? Expression.Parameter(typeof(T1), "a")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + _tables[0].Parameter = select.Parameters[0]; + this.InternalToChunk(select.Body, size, done); + } public DataTable ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select2Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select2Provider.cs index 79583b79..722e98ad 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select2Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select2Provider.cs @@ -94,7 +94,6 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -103,6 +102,12 @@ namespace FreeSql.Internal.CommonProvider _tables[0].Parameter ?? Expression.Parameter(typeof(T1), "a"), Expression.Parameter(typeof(T2), "b")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select3Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select3Provider.cs index 1ccd1d5c..a1a0482f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select3Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select3Provider.cs @@ -97,7 +97,6 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -107,6 +106,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T2), "b"), Expression.Parameter(typeof(T3), "c")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select4Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select4Provider.cs index c97b75b7..7ad1d657 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select4Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select4Provider.cs @@ -100,7 +100,6 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -111,6 +110,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T3), "c"), Expression.Parameter(typeof(T4), "d")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select5Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select5Provider.cs index 27d8a023..8d370dca 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select5Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select5Provider.cs @@ -103,7 +103,6 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -115,6 +114,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T4), "d"), Expression.Parameter(typeof(T5), "e")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select6Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select6Provider.cs index b5fb37c8..24017074 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select6Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select6Provider.cs @@ -106,7 +106,6 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -119,6 +118,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T5), "e"), Expression.Parameter(typeof(T6), "f")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select7Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select7Provider.cs index 52e1c4fb..be823cc8 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select7Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select7Provider.cs @@ -109,7 +109,6 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() { @@ -123,6 +122,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T6), "f"), Expression.Parameter(typeof(T7), "g")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select8Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select8Provider.cs index da9c40a5..4f24a463 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select8Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select8Provider.cs @@ -112,9 +112,7 @@ namespace FreeSql.Internal.CommonProvider for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; return this.InternalToList(select?.Body); } - List ISelect.ToList() => (this as ISelect).ToList(GetToListDtoSelector()); - Expression> GetToListDtoSelector() { return Expression.Lambda>( @@ -128,6 +126,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T7), "g"), Expression.Parameter(typeof(T8), "h")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select9Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select9Provider.cs index 5bfc03a1..33625d93 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select9Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select9Provider.cs @@ -130,6 +130,12 @@ namespace FreeSql.Internal.CommonProvider Expression.Parameter(typeof(T8), "h"), Expression.Parameter(typeof(T9), "i")); } + public void ToChunk(Expression> select, int size, Action>> done) + { + if (select == null || done == null) return; + for (var a = 0; a < select.Parameters.Count; a++) _tables[a].Parameter = select.Parameters[a]; + this.InternalToChunk(select.Body, size, done); + } DataTable ISelect.ToDataTable(Expression> select) {