From 5f987496526d178cca89f5a72ff27e8b5cdcc259 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Fri, 10 Apr 2020 02:28:33 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E8=B0=83=E6=95=B4=20ISelect=20linq=20to=20?= =?UTF-8?q?sql=20=E5=92=8C=20queryable=20=E5=AE=9E=E7=8E=B0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E7=A7=BB=E8=87=B3=20FreeSql.Extensions.Linq=EF=BC=9B#?= =?UTF-8?q?260?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FreeSql.Extensions.Linq.csproj | 37 +++ .../FreeSql.Extensions.Linq.xml | 41 +++ .../FreeSqlExtensionsLinq.cs | 131 ++++++++ .../QueryableProvider.cs | 279 ++++++++++++++++++ Extensions/FreeSql.Extensions.Linq/key.snk | Bin 0 -> 596 bytes .../Repository/Repository/BaseRepository.cs | 8 +- .../Repository/BaseRepositoryAsync.cs | 8 +- .../FreeSql.Tests.Provider.Odbc/UnitTest1.cs | 11 - .../FreeSql.Tests/FreeSql.Tests.csproj | 1 + .../Queryable/QueryableLinqToSqlTests.cs | 202 +++++++++++++ .../FreeSql.Tests/Queryable/QueryableTest.cs | 122 +++++++- FreeSql.sln | 15 + FreeSql/FreeSql.xml | 33 --- FreeSql/Interface/Curd/ISelect/ILinqToSql.cs | 31 -- FreeSql/Interface/Curd/ISelect/ISelect1.cs | 10 +- FreeSql/Internal/CommonExpression.cs | 43 +-- .../CommonProvider/CodeFirstProvider.cs | 3 +- .../SelectProvider/QueryableProvider.cs | 107 ------- .../SelectProvider/Select0Provider.cs | 113 +++---- .../SelectProvider/Select1Provider.cs | 122 ++------ .../SelectProvider/SelectGroupingProvider.cs | 105 +++---- 21 files changed, 987 insertions(+), 435 deletions(-) create mode 100644 Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj create mode 100644 Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml create mode 100644 Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs create mode 100644 Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs create mode 100644 Extensions/FreeSql.Extensions.Linq/key.snk create mode 100644 FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs delete mode 100644 FreeSql/Interface/Curd/ISelect/ILinqToSql.cs delete mode 100644 FreeSql/Internal/CommonProvider/SelectProvider/QueryableProvider.cs diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj new file mode 100644 index 00000000..202f9ed5 --- /dev/null +++ b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.0;net45;net40 + 1.4.0-preview20200410 + true + YeXiangQin + FreeSql 扩展包,实现 linq queryable 和 linq to sql 语法进行开发. + https://github.com/2881099/FreeSql + https://github.com/2881099/FreeSql + git + MIT + FreeSql;ORM + $(AssemblyName) + logo.png + $(AssemblyName) + true + true + true + key.snk + false + + + + + + + + FreeSql.Extensions.Linq.xml + 3 + + + + + + + diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml new file mode 100644 index 00000000..19871dd2 --- /dev/null +++ b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml @@ -0,0 +1,41 @@ + + + + FreeSql.Extensions.Linq + + + + + 将 ISelect<T1> 转换为 IQueryable<T1> + 此方法主要用于扩展,比如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象 + 注意:IQueryable 方法污染较为严重,请尽量避免此转换 + + + + + + 【linq to sql】专用扩展方法,不建议直接使用 + + + + + 【linq to sql】专用扩展方法,不建议直接使用 + + + + + 【linq to sql】专用扩展方法,不建议直接使用 + + + + + 【linq to sql】专用扩展方法,不建议直接使用 + + + + + 【linq to sql】专用扩展方法,不建议直接使用 + + + + diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs b/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs new file mode 100644 index 00000000..de256cec --- /dev/null +++ b/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs @@ -0,0 +1,131 @@ +using FreeSql; +using FreeSql.Extensions.Linq; +using FreeSql.Internal; +using FreeSql.Internal.CommonProvider; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Text.RegularExpressions; + +public static class FreeSqlExtensionsLinqSql +{ + + /// + /// 将 ISelect<T1> 转换为 IQueryable<T1> + /// 此方法主要用于扩展,比如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象 + /// 注意:IQueryable 方法污染较为严重,请尽量避免此转换 + /// + /// + public static IQueryable AsQueryable(this ISelect that) where T1 : class + { + return new QueryableProvider(that as Select1Provider); + } + + /// + /// 【linq to sql】专用扩展方法,不建议直接使用 + /// + public static ISelect Select(this ISelect that, Expression> select) where T1 : class where TReturn : class + { + var s1p = that as Select1Provider; + if (typeof(TReturn) == typeof(T1)) return that as ISelect; + s1p._tables[0].Parameter = select.Parameters[0]; + s1p._selectExpression = select.Body; + if (s1p._orm.CodeFirst.IsAutoSyncStructure) + (s1p._orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TReturn)); //._dicSyced.TryAdd(typeof(TReturn), true); + var ret = s1p._orm.Select() as Select1Provider; + Select0Provider.CopyData(s1p, ret, null); + return ret; + } + /// + /// 【linq to sql】专用扩展方法,不建议直接使用 + /// + public static ISelect Join(this ISelect that, ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) where T1 : class where TInner : class where TResult : class + { + var s1p = that as Select1Provider; + InternalJoin2(s1p, outerKeySelector, innerKeySelector, resultSelector); + if (typeof(TResult) == typeof(T1)) return that as ISelect; + s1p._selectExpression = resultSelector.Body; + if (s1p._orm.CodeFirst.IsAutoSyncStructure) + (s1p._orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TResult)); //._dicSyced.TryAdd(typeof(TResult), true); + var ret = s1p._orm.Select() as Select1Provider; + Select0Provider.CopyData(s1p, ret, null); + return ret; + } + internal static void InternalJoin2(Select1Provider s1p, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) where T1 : class + { + s1p._tables[0].Parameter = resultSelector.Parameters[0]; + s1p._commonExpression.ExpressionLambdaToSql(outerKeySelector, new CommonExpression.ExpTSC { _tables = s1p._tables }); + s1p.InternalJoin(Expression.Lambda(typeof(Func<,,>).MakeGenericType(typeof(T1), innerKeySelector.Parameters[0].Type, typeof(bool)), + Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), + new[] { outerKeySelector.Parameters[0], innerKeySelector.Parameters[0] } + ), SelectTableInfoType.InnerJoin); + } + + /// + /// 【linq to sql】专用扩展方法,不建议直接使用 + /// + public static ISelect GroupJoin(this ISelect that, ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) where T1 : class where TInner : class where TResult : class + { + var s1p = that as Select1Provider; + InternalJoin2(s1p, outerKeySelector, innerKeySelector, resultSelector); + if (typeof(TResult) == typeof(T1)) return that as ISelect; + s1p._selectExpression = resultSelector.Body; + if (s1p._orm.CodeFirst.IsAutoSyncStructure) + (s1p._orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TResult)); //._dicSyced.TryAdd(typeof(TResult), true); + var ret = s1p._orm.Select() as Select1Provider; + Select0Provider.CopyData(s1p, ret, null); + return ret; + } + /// + /// 【linq to sql】专用扩展方法,不建议直接使用 + /// + public static ISelect SelectMany(this ISelect that, Expression>> collectionSelector, Expression> resultSelector) where T1 : class where TCollection : class where TResult : class + { + var s1p = that as Select1Provider; + InternalSelectMany2(s1p, collectionSelector, resultSelector); + if (typeof(TResult) == typeof(T1)) return that as ISelect; + s1p._selectExpression = resultSelector.Body; + if (s1p._orm.CodeFirst.IsAutoSyncStructure) + (s1p._orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TResult)); //._dicSyced.TryAdd(typeof(TResult), true); + var ret = s1p._orm.Select() as Select1Provider; + Select0Provider.CopyData(s1p, ret, null); + return ret; + } + internal static void InternalSelectMany2(Select1Provider s1p, LambdaExpression collectionSelector, LambdaExpression resultSelector) where T1 : class + { + SelectTableInfo find = null; + if (collectionSelector.Body.NodeType == ExpressionType.Call) + { + var callExp = collectionSelector.Body as MethodCallExpression; + if (callExp.Method.Name == "DefaultIfEmpty" && callExp.Method.GetGenericArguments().Any()) + { + find = s1p._tables.Where((a, idx) => idx > 0 && a.Type == SelectTableInfoType.InnerJoin && a.Table.Type == callExp.Method.GetGenericArguments()[0]).LastOrDefault(); + if (find != null) + { + if (!string.IsNullOrEmpty(find.On)) find.On = Regex.Replace(find.On, $@"\b{find.Alias}\.", $"{resultSelector.Parameters[1].Name}."); + if (!string.IsNullOrEmpty(find.NavigateCondition)) find.NavigateCondition = Regex.Replace(find.NavigateCondition, $@"\b{find.Alias}\.", $"{resultSelector.Parameters[1].Name}."); + find.Type = SelectTableInfoType.LeftJoin; + find.Alias = resultSelector.Parameters[1].Name; + find.Parameter = resultSelector.Parameters[1]; + } + } + } + if (find == null) + { + var tb = s1p._commonUtils.GetTableByEntity(resultSelector.Parameters[1].Type); + if (tb == null) throw new Exception($"SelectMany 错误的类型:{resultSelector.Parameters[1].Type.FullName}"); + s1p._tables.Add(new SelectTableInfo { Alias = resultSelector.Parameters[1].Name, AliasInit = resultSelector.Parameters[1].Name, Parameter = resultSelector.Parameters[1], Table = tb, Type = SelectTableInfoType.From }); + } + } + + /// + /// 【linq to sql】专用扩展方法,不建议直接使用 + /// + public static ISelect DefaultIfEmpty(this ISelect that) where T1 : class + { + return that; + } +} diff --git a/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs b/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs new file mode 100644 index 00000000..892e1cf8 --- /dev/null +++ b/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs @@ -0,0 +1,279 @@ +using FreeSql.Internal; +using FreeSql.Internal.CommonProvider; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace FreeSql.Extensions.Linq +{ + class QueryableProvider : IQueryable, IOrderedQueryable where TSource : class + { + private Expression _expression; + private IQueryProvider _provider; + private Select1Provider _select; + + public QueryableProvider(Select1Provider select) + { + _select = select; + _expression = Expression.Constant(this); + _provider = new QueryProvider(_select); + } + public QueryableProvider(Expression expression, IQueryProvider provider, Select1Provider select) + { + _select = select; + _expression = expression; + _provider = provider; + } + + public IEnumerator GetEnumerator() + { + var result = _provider.Execute>(_expression); + if (result == null) + yield break; + foreach (var item in result) + { + yield return item; + } + } + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + + public Type ElementType => typeof(QueryableProvider); + public Expression Expression => _expression; + public IQueryProvider Provider => _provider; + } + + class QueryProvider : IQueryProvider where TSource : class + { + private Select1Provider _select; + + public QueryProvider(Select1Provider select) + { + _select = select; + } + + public IQueryable CreateQuery(Expression expression) + { + if (typeof(TElement) != typeof(TCurrent)) + return new QueryableProvider(expression, new QueryProvider(_select), _select); + + return new QueryableProvider(expression, this, _select); + } + public IQueryable CreateQuery(Expression expression) => throw new NotImplementedException(); + + public TResult Execute(Expression expression) + { + var stackCallExps = new Stack(); + var callExp = expression as MethodCallExpression; + while(callExp != null) + { + stackCallExps.Push(callExp); + callExp = callExp?.Arguments.FirstOrDefault() as MethodCallExpression; + } + + SelectGroupingProvider groupBy = null; + var isfirst = false; + while (stackCallExps.Any()) + { + callExp = stackCallExps.Pop(); + TResult throwCallExp(string message) => throw new Exception($"FreeSql Queryable 解析出错,执行的方法 {callExp.Method.Name} {message}"); + if (callExp.Method.DeclaringType != typeof(Queryable)) return throwCallExp($"必须属于 System.Linq.Enumerable"); + + TResult tplMaxMinAvgSum(string method) { + if (callExp.Arguments.Count == 2) + { + var avgParam = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression; + return (TResult)Utils.GetDataReaderValue(typeof(TResult), + _select.GetType().GetMethod(method).MakeGenericMethod(avgParam.ReturnType).Invoke(_select, new object[] { avgParam })); + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + } + TResult tplOrderBy(string method, bool isDescending) + { + if (callExp.Arguments.Count == 2) + { + var arg1 = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression; + _select.OrderByReflection(arg1, isDescending); + return default(TResult); + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + } + switch (callExp.Method.Name) + { + case "Any": + if (callExp.Arguments.Count == 2) _select.Where((Expression>)(callExp.Arguments[1] as UnaryExpression)?.Operand); + return (TResult)(object)_select.Any(); + case "AsQueryable": + break; + + case "Max": return tplMaxMinAvgSum("Max"); + case "Min": return tplMaxMinAvgSum("Min"); + case "Sum": return tplMaxMinAvgSum("Sum"); + case "Average": return tplMaxMinAvgSum("Avg"); + + case "Concat": + return throwCallExp(" 不支持"); + case "Contains": + if (callExp.Arguments.Count == 2) + { + var dywhere = (callExp.Arguments[1] as ConstantExpression)?.Value as TSource; + if (dywhere == null) return throwCallExp($" 参数值不能为 null"); + _select.WhereDynamic(dywhere); + return (TResult)(object)_select.Any(); + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + case "Count": + if (callExp.Arguments.Count == 2) _select.Where((Expression>)(callExp.Arguments[1] as UnaryExpression)?.Operand); + return (TResult)Utils.GetDataReaderValue(typeof(TResult), _select.Count()); + + case "Distinct": + if (callExp.Arguments.Count == 1) + { + _select.Distinct(); + break; + } + return throwCallExp(" 不支持"); + + case "ElementAt": + case "ElementAtOrDefault": + _select.Offset((int)(callExp.Arguments[1] as ConstantExpression)?.Value); + _select.Limit(1); + isfirst = true; + break; + case "First": + case "FirstOrDefault": + case "Single": + case "SingleOrDefault": + if (callExp.Arguments.Count == 2) _select.Where((Expression>)(callExp.Arguments[1] as UnaryExpression)?.Operand); + _select.Limit(1); + isfirst = true; + break; + + case "OrderBy": + tplOrderBy("OrderByReflection", false); + break; + case "OrderByDescending": + tplOrderBy("OrderByReflection", true); + break; + case "ThenBy": + tplOrderBy("OrderByReflection", false); + break; + case "ThenByDescending": + tplOrderBy("OrderByReflection", true); + break; + + case "Where": + var whereParam = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression; + if (whereParam.Parameters.Count == 1) + { + if (groupBy != null) groupBy.InternalHaving(whereParam); + else _select.InternalWhere(whereParam); + break; + } + return throwCallExp(" 不支持"); + + case "Skip": + _select.Offset((int)(callExp.Arguments[1] as ConstantExpression)?.Value); + break; + case "Take": + _select.Limit((int)(callExp.Arguments[1] as ConstantExpression)?.Value); + break; + + case "ToList": + if (callExp.Arguments.Count == 1) + return (TResult)(object)_select.ToList(); + return throwCallExp(" 不支持"); + + + case "Select": + var selectParam = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression; + if (selectParam.Parameters.Count == 1) + { + _select._selectExpression = selectParam; + break; + } + return throwCallExp(" 不支持"); + + case "Join": + if (callExp.Arguments.Count == 5) + { + var arg2 = (callExp.Arguments[2] as UnaryExpression)?.Operand as LambdaExpression; + var arg3 = (callExp.Arguments[3] as UnaryExpression)?.Operand as LambdaExpression; + var arg4 = (callExp.Arguments[4] as UnaryExpression)?.Operand as LambdaExpression; + FreeSqlExtensionsLinqSql.InternalJoin2(_select, arg2, arg3, arg4); + _select._selectExpression = arg4.Body; + break; + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + + case "GroupJoin": + if (callExp.Arguments.Count == 5) + { + var arg2 = (callExp.Arguments[2] as UnaryExpression)?.Operand as LambdaExpression; + var arg3 = (callExp.Arguments[3] as UnaryExpression)?.Operand as LambdaExpression; + var arg4 = (callExp.Arguments[4] as UnaryExpression)?.Operand as LambdaExpression; + FreeSqlExtensionsLinqSql.InternalJoin2(_select, arg2, arg3, arg4); + _select._selectExpression = arg4.Body; + break; + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + + case "SelectMany": + if (callExp.Arguments.Count == 3) + { + var arg1 = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression; + var arg2 = (callExp.Arguments[2] as UnaryExpression)?.Operand as LambdaExpression; + FreeSqlExtensionsLinqSql.InternalSelectMany2(_select, arg1, arg2); + _select._selectExpression = arg2.Body; + break; + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + + case "DefaultIfEmpty": + break; + + case "Last": + case "LastOrDefault": + return throwCallExp(" 不支持"); + + case "GroupBy": + return throwCallExp(" 不支持"); + + if (callExp.Arguments.Count == 2) //TODO: 待实现 + { + var arg1 = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression; + + var map = new ReadAnonymousTypeInfo(); + var field = new StringBuilder(); + var index = -10000; //临时规则,不返回 as1 + + _select._commonExpression.ReadAnonymousField(_select._tables, field, map, ref index, arg1, null, _select._whereCascadeExpression, false); //不走 DTO 映射 + var sql = field.ToString(); + _select.GroupBy(sql.Length > 0 ? sql.Substring(2) : null); + groupBy = new SelectGroupingProvider(_select._orm, _select, map, sql, _select._commonExpression, _select._tables); + break; + } + return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法"); + + default: + return throwCallExp(" 不支持"); + } + } + if (isfirst) + { + _select.Limit(1); + if (_select._selectExpression != null) + return (TResult)(object)_select.InternalToList(_select._selectExpression).FirstOrDefault(); + return (TResult)(object)_select.ToList().FirstOrDefault(); + } + if (_select._selectExpression != null) + return (TResult)(object)_select.InternalToList(_select._selectExpression); + return (TResult)(object)_select.ToList(); + } + public object Execute(Expression expression) => throw new NotImplementedException(); + } +} diff --git a/Extensions/FreeSql.Extensions.Linq/key.snk b/Extensions/FreeSql.Extensions.Linq/key.snk new file mode 100644 index 0000000000000000000000000000000000000000..e580bc8d5d64e7c5a0c62b971545d38cfbe7d837 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096c(W3|+clf|4d2=6Xc+R`Gd@9@k@Meh} zR8`}1=JPk=q?Zlr?i$1O?SgX-{{&z z|LRF?-aWODhAO}h_7M!wz}uPXx}n-g{((r9{{%_ z4)%gVXcj;Ru@GYAIZI@e#GBtO#O5m-Qr4X_lbAV}=qNRkd0^`@I6i9k`wSe@ZPxVo zk;MXig(S-cYHE!0GWWlp7EH@E!WkF6jS+3z4rvW0%Sq;U1bq`B9*HNJjxo*23*7Vw zHyt>{2CR~8==`lYLgAmwsXPXYZ_AEAKy|PuUz0(G)L1xO{{*n6Bn?mV~QKg!055THihpc>GOh(U-NgO?4_DzY4uq!p9=Q;`G i9;v3GkC674Mbx4_b$)?7a0%Z%&zUjzbGs@!l^s|B literal 0 HcmV?d00001 diff --git a/FreeSql.DbContext/Repository/Repository/BaseRepository.cs b/FreeSql.DbContext/Repository/Repository/BaseRepository.cs index cc8ec1bf..0670666f 100644 --- a/FreeSql.DbContext/Repository/Repository/BaseRepository.cs +++ b/FreeSql.DbContext/Repository/Repository/BaseRepository.cs @@ -73,7 +73,7 @@ namespace FreeSql public ISelect Where(Expression> exp) => _dbset.OrmSelectInternal(null).Where(exp); public ISelect WhereIf(bool condition, Expression> exp) => _dbset.OrmSelectInternal(null).WhereIf(condition, exp); - public int Delete(Expression> predicate) + public virtual int Delete(Expression> predicate) { var delete = _dbset.OrmDeleteInternal(null).Where(predicate); var sql = delete.ToSql(); @@ -82,12 +82,12 @@ namespace FreeSql return affrows; } - public int Delete(TEntity entity) + public virtual int Delete(TEntity entity) { _dbset.Remove(entity); return _db.SaveChanges(); } - public int Delete(IEnumerable entitys) + public virtual int Delete(IEnumerable entitys) { _dbset.RemoveRange(entitys); return _db.SaveChanges(); @@ -157,7 +157,7 @@ namespace FreeSql return ret; } - public int Delete(TKey id) => Delete(CheckTKeyAndReturnIdEntity(id)); + public virtual int Delete(TKey id) => Delete(CheckTKeyAndReturnIdEntity(id)); public TEntity Find(TKey id) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOne(); public TEntity Get(TKey id) => Find(id); } diff --git a/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs b/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs index c3e5dd42..3cded670 100644 --- a/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs +++ b/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs @@ -13,7 +13,7 @@ namespace FreeSql where TEntity : class { - async public Task DeleteAsync(Expression> predicate) + async virtual public Task DeleteAsync(Expression> predicate) { var delete = _dbset.OrmDeleteInternal(null).Where(predicate); var sql = delete.ToSql(); @@ -22,12 +22,12 @@ namespace FreeSql return affrows; } - public Task DeleteAsync(TEntity entity) + public virtual Task DeleteAsync(TEntity entity) { _dbset.Remove(entity); return _db.SaveChangesAsync(); } - public Task DeleteAsync(IEnumerable entitys) + public virtual Task DeleteAsync(IEnumerable entitys) { _dbset.RemoveRange(entitys); return _db.SaveChangesAsync(); @@ -73,7 +73,7 @@ namespace FreeSql partial class BaseRepository { - public Task DeleteAsync(TKey id) => DeleteAsync(CheckTKeyAndReturnIdEntity(id)); + public virtual Task DeleteAsync(TKey id) => DeleteAsync(CheckTKeyAndReturnIdEntity(id)); public Task FindAsync(TKey id) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOneAsync(); public Task GetAsync(TKey id) => FindAsync(id); } diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/UnitTest1.cs b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/UnitTest1.cs index 5f040d3c..4f26b4ac 100644 --- a/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/UnitTest1.cs +++ b/FreeSql.Tests/FreeSql.Tests.Provider.Odbc/UnitTest1.cs @@ -563,8 +563,6 @@ WHERE ROWNUM < 11"; .UpdateColumns(x => new { x.Status, x.CategoryId, x.ArticleTitle }) .ToSql(); - var sqldddklist = g.mysql.Select().Select(a => new NewsArticleDto { }).ToList(); - var sql1111333 = g.mysql.Update() .SetSource(new Model2 { id = 1, Title = "xxx", Parent_id = 0 }) @@ -608,15 +606,6 @@ WHERE ROWNUM < 11"; .ToList(); var ttt1 = g.mysql.Select().Where(a => a.Childs.AsSelect().Any(b => b.Title == "111")).ToList(); - - var linqto1 = - from p in g.mysql.Select() - where p.Id >= 0 - // && p.OrderDetails.AsSelect().Where(c => c.Id > 10).Any() - orderby p.Id descending - orderby p.CustomerName ascending - select new { Name = p.CustomerName, Length = p.Id }; - var testpid1 = g.mysql.Insert().AppendData(new TestTypeInfo { Name = "Name" + DateTime.Now.ToString("yyyyMMddHHmmss") }).ExecuteIdentity(); g.mysql.Insert().AppendData(new TestInfo { Title = "Title" + DateTime.Now.ToString("yyyyMMddHHmmss"), CreateTime = DateTime.Now, TypeGuid = (int)testpid1 }).ExecuteAffrows(); diff --git a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj index e2398841..7fd0f8e7 100644 --- a/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj +++ b/FreeSql.Tests/FreeSql.Tests/FreeSql.Tests.csproj @@ -30,6 +30,7 @@ + diff --git a/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs b/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs new file mode 100644 index 00000000..f028f93a --- /dev/null +++ b/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs @@ -0,0 +1,202 @@ +using FreeSql.DataAnnotations; +using System; +using System.Linq; +using Xunit; + +namespace FreeSql.Tests.Linq +{ + + + class TestQueryableLinqToSql + { + public Guid id { get; set; } + + public string name { get; set; } + + public int click { get; set; } = 10; + + public DateTime createtime { get; set; } = DateTime.Now; + } + class TestQueryableLinqToSqlComment + { + public Guid id { get; set; } + + public Guid TestLinqToSqlId { get; set; } + public TestQueryableLinqToSql TEstLinqToSql { get; set; } + + public string text { get; set; } + + public DateTime createtime { get; set; } = DateTime.Now; + } + + public class QueryableLinqToSqlTests + { + + [Fact] + public void Where() + { + var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select().AsQueryable() + where a.id == item.id + select a).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1[0].id); + } + + [Fact] + public void Select() + { + var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select().AsQueryable() + where a.id == item.id + select new { a.id }).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1[0].id); + } + + [Fact] + public void GroupBy() + { + //var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + //g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + //var t1 = (from a in g.sqlite.Select().AsQueryable() + // where a.id == item.id + // group a by new { a.id, a.name } into g + // select new + // { + // g.Key.id, + // g.Key.name, + // cou = g.Count(), + // avg = g.Average(x => x.click), + // sum = g.Sum(x => x.click), + // max = g.Max(x => x.click), + // min = g.Min(x => x.click) + // }).ToList(); + //Assert.True(t1.Any()); + //Assert.Equal(item.id, t1.First().id); + } + + [Fact] + public void CaseWhen() + { + var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select().AsQueryable() + where a.id == item.id + select new + { + a.id, + a.name, + testsub = new + { + time = a.click > 10 ? "" : "Сڻ" + } + }).ToList(); + Assert.True(t1.Any()); + Assert.Equal(item.id, t1[0].id); + Assert.Equal("Сڻ", t1[0].testsub.time); + } + + [Fact] + public void Join() + { + var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + var comment = new TestQueryableLinqToSqlComment { TestLinqToSqlId = item.id, text = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(comment).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select().AsQueryable() + join b in g.sqlite.Select().AsQueryable() on a.id equals b.TestLinqToSqlId + select a).ToList(); + Assert.True(t1.Any()); + //Assert.Equal(item.id, t1[0].id); + + var t2 = (from a in g.sqlite.Select().AsQueryable() + join b in g.sqlite.Select().AsQueryable() on a.id equals b.TestLinqToSqlId + select new { a.id, bid = b.id }).ToList(); + Assert.True(t2.Any()); + //Assert.Equal(item.id, t2[0].id); + //Assert.Equal(comment.id, t2[0].bid); + + var t3 = (from a in g.sqlite.Select().AsQueryable() + join b in g.sqlite.Select().AsQueryable() on a.id equals b.TestLinqToSqlId + where a.id == item.id + select new { a.id, bid = b.id }).ToList(); + Assert.True(t3.Any()); + Assert.Equal(item.id, t3[0].id); + Assert.Equal(comment.id, t3[0].bid); + } + + [Fact] + public void LeftJoin() + { + var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + var comment = new TestQueryableLinqToSqlComment { TestLinqToSqlId = item.id, text = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(comment).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select().AsQueryable() + join b in g.sqlite.Select().AsQueryable() on a.id equals b.TestLinqToSqlId into temp + from tc in temp.DefaultIfEmpty() + select a).ToList(); + Assert.True(t1.Any()); + //Assert.Equal(item.id, t1[0].id); + + var t2 = (from a in g.sqlite.Select().AsQueryable() + join b in g.sqlite.Select().AsQueryable() on a.id equals b.TestLinqToSqlId into temp + from tc in temp.DefaultIfEmpty() + select new { a.id, bid = tc.id }).ToList(); + Assert.True(t2.Any()); + //Assert.Equal(item.id, t2[0].id); + //Assert.Equal(comment.id, t2[0].bid); + + var t3 = (from a in g.sqlite.Select().AsQueryable() + join b in g.sqlite.Select().AsQueryable() on a.id equals b.TestLinqToSqlId into temp + from tc in temp.DefaultIfEmpty() + where a.id == item.id + select new { a.id, bid = tc.id }).ToList(); + Assert.True(t3.Any()); + Assert.Equal(item.id, t3[0].id); + Assert.Equal(comment.id, t3[0].bid); + } + + [Fact] + public void From() + { + var item = new TestQueryableLinqToSql { name = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(item).ExecuteAffrows(); + var comment = new TestQueryableLinqToSqlComment { TestLinqToSqlId = item.id, text = Guid.NewGuid().ToString() }; + g.sqlite.Insert().AppendData(comment).ExecuteAffrows(); + + var t1 = (from a in g.sqlite.Select().AsQueryable() + from b in g.sqlite.Select().AsQueryable() + where a.id == b.TestLinqToSqlId + select a).ToList(); + Assert.True(t1.Any()); + //Assert.Equal(item.id, t1[0].id); + + var t2 = (from a in g.sqlite.Select().AsQueryable() + from b in g.sqlite.Select().AsQueryable() + where a.id == b.TestLinqToSqlId + select new { a.id, bid = b.id }).ToList(); + Assert.True(t2.Any()); + //Assert.Equal(item.id, t2[0].id); + //Assert.Equal(comment.id, t2[0].bid); + + var t3 = (from a in g.sqlite.Select().AsQueryable() + from b in g.sqlite.Select().AsQueryable() + where a.id == b.TestLinqToSqlId + where a.id == item.id + select new { a.id, bid = b.id }).ToList(); + Assert.True(t3.Any()); + Assert.Equal(item.id, t3[0].id); + Assert.Equal(comment.id, t3[0].bid); + } + } +} diff --git a/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableTest.cs b/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableTest.cs index 8dbedf6d..acae29f2 100644 --- a/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableTest.cs @@ -17,7 +17,7 @@ using System.Diagnostics; using System.IO; using System.Text; -namespace FreeSql.Tests +namespace FreeSql.Tests.Linq { public class QueryableTest { @@ -66,6 +66,126 @@ namespace FreeSql.Tests Assert.True(fsql.Select().AsQueryable().Any(a => a.id == sd[0].id)); Assert.False(fsql.Select().AsQueryable().Any(a => a.id == sd[0].id && sd[0].id == 0)); } + + [Fact] + public void Max() + { + var avg = fsql.Select().AsQueryable().Max(a => a.id); + Assert.True(avg > 0); + } + [Fact] + public void Min() + { + var avg = fsql.Select().AsQueryable().Min(a => a.id); + Assert.True(avg > 0); + } + [Fact] + public void Sum() + { + var avg = fsql.Select().AsQueryable().Sum(a => a.id); + Assert.True(avg > 0); + } + [Fact] + public void Average() + { + var avg = fsql.Select().AsQueryable().Average(a => a.id); + Assert.True(avg > 0); + } + + [Fact] + public void Contains() + { + Assert.True(fsql.Select().AsQueryable().Contains(new qt01 { id = 1 })); + Assert.False(fsql.Select().AsQueryable().Contains(new qt01 { id = 0 })); + } + + [Fact] + public void Distinct() + { + fsql.Select().AsQueryable().Distinct().Select(a => a.name).ToList(); + } + + [Fact] + public void ElementAt() + { + Assert.Equal(fsql.Select().Skip(1).First().id, fsql.Select().AsQueryable().ElementAt(1).id); + Assert.Equal(fsql.Select().Skip(2).First().id, fsql.Select().AsQueryable().ElementAt(2).id); + Assert.Equal(fsql.Select().Skip(1).First().id, fsql.Select().AsQueryable().ElementAtOrDefault(1).id); + Assert.Equal(fsql.Select().Skip(2).First().id, fsql.Select().AsQueryable().ElementAtOrDefault(2).id); + } + + [Fact] + public void First() + { + Assert.Equal(fsql.Select().First().id, fsql.Select().AsQueryable().First().id); + Assert.Equal(fsql.Select().First().id, fsql.Select().AsQueryable().FirstOrDefault().id); + } + + [Fact] + public void Single() + { + Assert.Equal(fsql.Select().First().id, fsql.Select().AsQueryable().Single().id); + Assert.Equal(fsql.Select().First().id, fsql.Select().AsQueryable().SingleOrDefault().id); + } + + [Fact] + public void OrderBy() + { + Assert.Equal(fsql.Select().OrderBy(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).Single().id); + Assert.Equal(fsql.Select().OrderBy(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).SingleOrDefault().id); + } + [Fact] + public void OrderByDescending() + { + Assert.Equal(fsql.Select().OrderByDescending(a => a.id).First().id, fsql.Select().AsQueryable().OrderByDescending(a => a.id).Single().id); + Assert.Equal(fsql.Select().OrderByDescending(a => a.id).First().id, fsql.Select().AsQueryable().OrderByDescending(a => a.id).SingleOrDefault().id); + } + [Fact] + public void ThenBy() + { + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderBy(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).OrderBy(a => a.id).Single().id); + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderBy(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).OrderBy(a => a.id).SingleOrDefault().id); + + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderBy(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).ThenBy(a => a.id).Single().id); + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderBy(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).ThenBy(a => a.id).SingleOrDefault().id); + } + [Fact] + public void ThenByDescending() + { + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderByDescending(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).OrderByDescending(a => a.id).Single().id); + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderByDescending(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).OrderByDescending(a => a.id).SingleOrDefault().id); + + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderByDescending(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).ThenByDescending(a => a.id).Single().id); + Assert.Equal(fsql.Select().OrderBy(a => a.id).OrderByDescending(a => a.id).First().id, fsql.Select().AsQueryable().OrderBy(a => a.id).ThenByDescending(a => a.id).SingleOrDefault().id); + } + + [Fact] + public void Select() + { + Assert.Equal(fsql.Select().First(a => a.name), fsql.Select().AsQueryable().Select(a => a.name).Single()); + Assert.Equal(fsql.Select().First(a => new { a.name }).name, fsql.Select().AsQueryable().Select(a => new { a.name }).Single().name); + } + + [Fact] + public void Where() + { + Assert.Equal(fsql.Select().First(a => a.name), fsql.Select().AsQueryable().Select(a => a.name).Single()); + Assert.Equal(fsql.Select().First(a => new { a.name }).name, fsql.Select().AsQueryable().Select(a => new { a.name }).Single().name); + } + + [Fact] + public void Skip() + { + Assert.Equal(fsql.Select().Skip(2).First(a => a.name), fsql.Select().AsQueryable().Skip(2).Select(a => a.name).Single()); + Assert.Equal(fsql.Select().Skip(2).First(a => new { a.name }).name, fsql.Select().AsQueryable().Skip(2).Select(a => new { a.name }).Single().name); + } + + [Fact] + public void Take() + { + Assert.Equal(fsql.Select().Skip(2).First(a => a.name), fsql.Select().AsQueryable().Skip(2).Take(1).Select(a => a.name).ToList().FirstOrDefault()); + Assert.Equal(fsql.Select().Skip(2).First(a => new { a.name }).name, fsql.Select().AsQueryable().Skip(2).Take(1).Select(a => new { a.name }).ToList().FirstOrDefault().name); + } } } diff --git a/FreeSql.sln b/FreeSql.sln index 37d728c5..d7236979 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -76,6 +76,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.EfCoreFl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.All", "FreeSql.All\FreeSql.All.csproj", "{933115AD-769C-4FBE-B000-2E8CF2292377}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.Linq", "Extensions\FreeSql.Extensions.Linq\FreeSql.Extensions.Linq.csproj", "{57B3F5B0-D46A-4442-8EC6-9A9A784404B7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -458,6 +460,18 @@ Global {933115AD-769C-4FBE-B000-2E8CF2292377}.Release|x64.Build.0 = Release|Any CPU {933115AD-769C-4FBE-B000-2E8CF2292377}.Release|x86.ActiveCfg = Release|Any CPU {933115AD-769C-4FBE-B000-2E8CF2292377}.Release|x86.Build.0 = Release|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Debug|x64.ActiveCfg = Debug|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Debug|x64.Build.0 = Debug|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Debug|x86.ActiveCfg = Debug|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Debug|x86.Build.0 = Debug|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Release|Any CPU.Build.0 = Release|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Release|x64.ActiveCfg = Release|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Release|x64.Build.0 = Release|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Release|x86.ActiveCfg = Release|Any CPU + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -484,6 +498,7 @@ Global {6A3A4470-7DF7-411B-AAD7-755D7A9DB5A4} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} {B397A761-F646-41CF-A160-AB6C05DAF2FB} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {773D5B63-DE6E-46DB-AF16-6FB1C1352B3F} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} + {57B3F5B0-D46A-4442-8EC6-9A9A784404B7} = {4A92E8A6-9A6D-41A1-9CDA-DE10899648AA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98} diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 83ccee42..77e5b1ba 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -993,31 +993,6 @@ 使用属性名作为字段别名 - - - 【linq to sql】专用方法,不建议直接使用 - - - - - 【linq to sql】专用方法,不建议直接使用 - - - - - 【linq to sql】专用方法,不建议直接使用 - - - - - 【linq to sql】专用方法,不建议直接使用 - - - - - 【linq to sql】专用方法,不建议直接使用 - - 指定事务对象 @@ -1748,14 +1723,6 @@ SQL语句 - - - 将 ISelect<T1> 转换为 IQueryable<T1> - 此方法主要用于扩展,比如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象 - 注意:IQueryable 方法污染较为严重,请尽量避免此转换 - - - 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com") diff --git a/FreeSql/Interface/Curd/ISelect/ILinqToSql.cs b/FreeSql/Interface/Curd/ISelect/ILinqToSql.cs deleted file mode 100644 index bba2d4e0..00000000 --- a/FreeSql/Interface/Curd/ISelect/ILinqToSql.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Text; - -namespace FreeSql -{ - public interface ILinqToSql where T1 : class - { - /// - /// 【linq to sql】专用方法,不建议直接使用 - /// - ISelect Select(Expression> select) where TReturn : class; - /// - /// 【linq to sql】专用方法,不建议直接使用 - /// - ISelect Join(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) where TInner : class where TResult : class; - /// - /// 【linq to sql】专用方法,不建议直接使用 - /// - ISelect GroupJoin(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) where TInner : class where TResult : class; - /// - /// 【linq to sql】专用方法,不建议直接使用 - /// - ISelect DefaultIfEmpty(); - /// - /// 【linq to sql】专用方法,不建议直接使用 - /// - ISelect SelectMany(Expression>> collectionSelector, Expression> resultSelector) where TCollection : class where TResult : class; - } -} diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index a6bb4622..859b48aa 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace FreeSql { - public interface ISelect : ISelect0, T1>, ILinqToSql where T1 : class + public interface ISelect : ISelect0, T1> where T1 : class { #if net40 @@ -378,13 +378,5 @@ namespace FreeSql /// SQL语句 /// ISelect WithSql(string sql); - - /// - /// 将 ISelect<T1> 转换为 IQueryable<T1> - /// 此方法主要用于扩展,比如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象 - /// 注意:IQueryable 方法污染较为严重,请尽量避免此转换 - /// - /// - IQueryable AsQueryable(); } } \ No newline at end of file diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 8df262b1..c91ecd75 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -1,17 +1,18 @@ -using FreeSql.Internal.Model; +using FreeSql.DataAnnotations; +using FreeSql.Internal.CommonProvider; +using FreeSql.Internal.Model; using System; using System.Collections; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Data.Common; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using FreeSql.DataAnnotations; using System.Threading; -using System.Globalization; namespace FreeSql.Internal { @@ -19,8 +20,8 @@ namespace FreeSql.Internal { public CommonUtils _common; - public CommonProvider.AdoProvider _ado => _adoPriv ?? (_adoPriv = _common._orm.Ado as CommonProvider.AdoProvider); - CommonProvider.AdoProvider _adoPriv; + public AdoProvider _ado => _adoPriv ?? (_adoPriv = _common._orm.Ado as AdoProvider); + AdoProvider _adoPriv; public CommonExpression(CommonUtils common) { _common = common; @@ -692,14 +693,6 @@ namespace FreeSql.Internal } if (callType.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`")) { - //if (exp3.Type == typeof(string) && exp3.Arguments.Any() && exp3.Arguments[0].NodeType == ExpressionType.Constant) { - // switch (exp3.Method.Name) { - // case "Sum": return $"sum({(exp3.Arguments[0] as ConstantExpression)?.Value})"; - // case "Avg": return $"avg({(exp3.Arguments[0] as ConstantExpression)?.Value})"; - // case "Max": return $"max({(exp3.Arguments[0] as ConstantExpression)?.Value})"; - // case "Min": return $"min({(exp3.Arguments[0] as ConstantExpression)?.Value})"; - // } - //} switch (exp3.Method.Name) { case "Count": return exp3.Arguments.Count == 0 ? "count(1)" : $"count({ExpressionLambdaToSql(exp3.Arguments[0], tsc)})"; @@ -819,11 +812,10 @@ namespace FreeSql.Internal if (fsql == null) fsql = Expression.Lambda(exp3tmp).Compile().DynamicInvoke(); fsqlType = fsql?.GetType(); if (fsqlType == null) break; - if (exp3.Method.Name != "ToList") - fsqlType.GetField("_limit", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(fsql, 1); - if (tsc.dbParams != null) - fsqlType.GetField("_params", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(fsql, tsc.dbParams); - fsqltables = fsqlType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(fsql) as List; + var fsqlSelect0 = fsql as Select0Provider; + if (exp3.Method.Name != "ToList") fsqlSelect0._limit = 1; + if (tsc.dbParams != null) fsqlSelect0._params = tsc.dbParams; + fsqltables = fsqlSelect0._tables; //fsqltables[0].Alias = $"{tsc._tables[0].Alias}_{fsqltables[0].Alias}"; if (fsqltables != tsc._tables) fsqltables.AddRange(tsc._tables.Select(a => new SelectTableInfo @@ -836,7 +828,7 @@ namespace FreeSql.Internal })); if (tsc.whereCascadeExpression?.Any() == true) { - var fsqlCascade = fsqlType.GetField("_whereCascadeExpression", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(fsql) as List; + var fsqlCascade = fsqlSelect0._whereCascadeExpression; if (fsqlCascade != tsc.whereCascadeExpression) fsqlCascade.AddRange(tsc.whereCascadeExpression); } @@ -1022,12 +1014,6 @@ namespace FreeSql.Internal break; } } - //var eleType = callType.GetElementType() ?? callType.GenericTypeArguments.FirstOrDefault(); - //if (eleType != null && typeof(IEnumerable<>).MakeGenericType(eleType).IsAssignableFrom(callType)) { //集合导航属性子查询 - // if (exp3.Method.Name == "Any") { //exists - - // } - //} other3Exp = ExpressionLambdaToSqlOther(exp3, tsc); if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp; if (exp3.IsParameter() == false) return formatSql(Expression.Lambda(exp3).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); @@ -1107,7 +1093,10 @@ namespace FreeSql.Internal break; case ExpressionType.MemberAccess: var expStackFirstMem = expStack.First() as MemberExpression; - if (expStackFirstMem.Expression?.NodeType == ExpressionType.Constant) firstValue = (expStackFirstMem.Expression as ConstantExpression)?.Value; + if (expStackFirstMem.Expression?.NodeType == ExpressionType.Constant) + firstValue = (expStackFirstMem.Expression as ConstantExpression)?.Value; + else + return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); break; } while (expStack.Any()) diff --git a/FreeSql/Internal/CommonProvider/CodeFirstProvider.cs b/FreeSql/Internal/CommonProvider/CodeFirstProvider.cs index bd348901..619f9d23 100644 --- a/FreeSql/Internal/CommonProvider/CodeFirstProvider.cs +++ b/FreeSql/Internal/CommonProvider/CodeFirstProvider.cs @@ -16,7 +16,6 @@ namespace FreeSql.Internal.CommonProvider public abstract partial class CodeFirstProvider : ICodeFirst { - public IFreeSql _orm; public CommonUtils _commonUtils; public CommonExpression _commonExpression; @@ -78,7 +77,7 @@ namespace FreeSql.Internal.CommonProvider _dicSynced.TryAdd(entityType, trydic = new ConcurrentDictionary()); return trydic; } - internal void _dicSycedTryAdd(Type entityType, string tableName = null) => + public void _dicSycedTryAdd(Type entityType, string tableName = null) => _dicSycedGetOrAdd(entityType).TryAdd(GetTableNameLowerOrUpper(tableName), true); public void SyncStructure() => diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/QueryableProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/QueryableProvider.cs deleted file mode 100644 index bf52e097..00000000 --- a/FreeSql/Internal/CommonProvider/SelectProvider/QueryableProvider.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; - -namespace FreeSql.Internal.CommonProvider -{ - class QueryableProvider : IQueryable - { - private Expression _expression; - private IQueryProvider _provider; - private object _select; - private CommonExpression _commonExpression; - - public QueryableProvider(object select) - { - _select = select; - _commonExpression = _select.GetType().GetField("_commonExpression", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_select) as CommonExpression; - _expression = Expression.Constant(this); - _provider = new QueryProvider(_select, _commonExpression); - } - public QueryableProvider(Expression expression, IQueryProvider provider, object select, CommonExpression commonExpression) - { - _select = select; - _commonExpression = commonExpression; - _expression = expression; - _provider = provider; - } - - public IEnumerator GetEnumerator() - { - var result = _provider.Execute>(_expression); - if (result == null) - yield break; - foreach (var item in result) - { - yield return item; - } - } - IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); - - public Type ElementType => typeof(QueryableProvider); - public Expression Expression => _expression; - public IQueryProvider Provider => _provider; - } - - class QueryProvider : IQueryProvider - { - private object _select; - private CommonExpression _commonExpression; - - public QueryProvider(object select, CommonExpression commonExpression) - { - _select = select; - _commonExpression = commonExpression; - } - - public IQueryable CreateQuery(Expression expression) - { - IQueryable query = new QueryableProvider(expression, this, _select, _commonExpression); - return query; - } - public IQueryable CreateQuery(Expression expression) => throw new NotImplementedException(); - - - public TResult Execute(Expression expression) - { - var methodExp = expression as MethodCallExpression; - while (methodExp != null) - { - switch (methodExp.Method.Name) - { - case "First": - case "FirstOrDefault": - _select.GetType().GetMethod("Limit", new[] { typeof(int) }).Invoke(_select, new object[] { 1 }); - break; - default: - var selectMethod = _select.GetType().GetMethod(methodExp.Method.Name, methodExp.Arguments.Where((a, b) => b > 0).Select(a => a.Type).ToArray()); - if (selectMethod == null) throw new Exception($"无法找到 ISelect.{methodExp.Method.Name}({string.Join(", ", methodExp.Arguments.Select(a => a.Type.FullName))}) 方法"); - - var selectArgs = methodExp.Arguments.Where((a, b) => b > 0).Select(a => - { - switch (a.NodeType) - { - case ExpressionType.Lambda: return (object)a; - default: return Expression.Lambda(a).Compile().DynamicInvoke(); - } - }).ToArray(); - selectMethod.Invoke(_select, selectArgs); - break; - } - methodExp = methodExp.Arguments.FirstOrDefault() as MethodCallExpression; - } - var resultType = typeof(TResult); - var resultTypeIsList = typeof(IList).IsAssignableFrom(resultType); - if (resultTypeIsList) resultType = resultType.GetGenericArguments()[0]; - var ret = _select.GetType().GetMethod(resultTypeIsList ? "ToList" : "First", new Type[0]) - .MakeGenericMethod(resultType) - .Invoke(_select, new object[0]); - return (TResult)ret; - } - public object Execute(Expression expression) => throw new NotImplementedException(); - } -} diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 950b1383..d25664fd 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -16,33 +16,32 @@ using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { - public abstract partial class Select0Provider : ISelect0 where TSelect : class where T1 : class + public abstract partial class Select0Provider { - - protected int _limit, _skip; - protected string _select = "SELECT ", _orderby, _groupby, _having; - protected StringBuilder _where = new StringBuilder(); - protected List _params = new List(); - internal protected List _tables = new List(); - protected List> _tableRules = new List>(); - protected Func _aliasRule; - protected string _tosqlAppendContent; - protected StringBuilder _join = new StringBuilder(); - internal protected IFreeSql _orm; - protected CommonUtils _commonUtils; - protected CommonExpression _commonExpression; - protected DbTransaction _transaction; - protected DbConnection _connection; - internal protected Action _trackToList; - internal protected List> _includeToList = new List>(); + public int _limit, _skip; + public string _select = "SELECT ", _orderby, _groupby, _having; + public StringBuilder _where = new StringBuilder(); + public List _params = new List(); + public List _tables = new List(); + public List> _tableRules = new List>(); + public Func _aliasRule; + public string _tosqlAppendContent; + public StringBuilder _join = new StringBuilder(); + public IFreeSql _orm; + public CommonUtils _commonUtils; + public CommonExpression _commonExpression; + public DbTransaction _transaction; + public DbConnection _connection; + public Action _trackToList; + public List> _includeToList = new List>(); #if net40 #else - protected List> _includeToListAsync = new List>(); + public List> _includeToListAsync = new List>(); #endif - protected bool _distinct; - protected Expression _selectExpression; - protected List _whereCascadeExpression = new List(); - protected List _whereGlobalFilter; + public bool _distinct; + public Expression _selectExpression; + public List _whereCascadeExpression = new List(); + public List _whereGlobalFilter; int _disposeCounter; ~Select0Provider() @@ -64,24 +63,25 @@ namespace FreeSql.Internal.CommonProvider _whereGlobalFilter = _orm.GlobalFilter.GetFilters(); _whereCascadeExpression.AddRange(_whereGlobalFilter.Select(a => a.Where)); } - public static void CopyData(Select0Provider from, object to, ReadOnlyCollection lambParms) + + public static void CopyData(Select0Provider from, Select0Provider to, ReadOnlyCollection lambParms) { - var toType = to?.GetType(); - if (toType == null) return; - toType.GetField("_limit", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._limit); - toType.GetField("_skip", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._skip); - toType.GetField("_select", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._select); - toType.GetField("_orderby", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._orderby); - toType.GetField("_groupby", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._groupby); - toType.GetField("_having", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._having); - toType.GetField("_where", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new StringBuilder().Append(from._where.ToString())); - toType.GetField("_params", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._params.ToArray())); + if (to == null) return; + to._limit = from._limit; + to._skip = from._skip; + to._select = from._select; + to._orderby = from._orderby; + to._groupby = from._groupby; + to._having = from._having; + to._where = new StringBuilder().Append(from._where.ToString()); + to._params = new List(from._params.ToArray()); + if (lambParms == null) - toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._tables.ToArray())); + to._tables = new List(from._tables.ToArray()); else { var findedIndexs = new List(); - var _multiTables = toType.GetField("_tables", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(to) as List; + var _multiTables = to._tables; _multiTables[0] = from._tables[0]; for (var a = 1; a < lambParms.Count; a++) { @@ -103,26 +103,29 @@ namespace FreeSql.Internal.CommonProvider _multiTables.Add(from._tables[a]); } } - toType.GetField("_tableRules", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List>(from._tableRules.ToArray())); - toType.GetField("_aliasRule", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._aliasRule); - toType.GetField("_join", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new StringBuilder().Append(from._join.ToString())); - //toType.GetField("_orm", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._orm); - //toType.GetField("_commonUtils", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._commonUtils); - //toType.GetField("_commonExpression", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._commonExpression); - toType.GetField("_transaction", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._transaction); - toType.GetField("_connection", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._connection); - toType.GetField("_trackToList", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._trackToList); - toType.GetField("_includeToList", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List>(from._includeToList.ToArray())); + to._tableRules = new List>(from._tableRules.ToArray()); + to._aliasRule = from._aliasRule; + to._join = new StringBuilder().Append(from._join.ToString()); + //to._orm = from._orm; + //to._commonUtils = from._commonUtils; + //to._commonExpression = from._commonExpression; + to._transaction = from._transaction; + to._connection = from._connection; + to._trackToList = from._trackToList; + to._includeToList = new List>(from._includeToList.ToArray()); #if net40 #else - toType.GetField("_includeToListAsync", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List>(from._includeToListAsync.ToArray())); + to._includeToListAsync = new List>(from._includeToListAsync.ToArray()); #endif - toType.GetField("_distinct", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._distinct); - toType.GetField("_selectExpression", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._selectExpression); - toType.GetField("_whereCascadeExpression", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._whereCascadeExpression.ToArray())); - toType.GetField("_whereGlobalFilter", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, new List(from._whereGlobalFilter.ToArray())); + to._distinct = from._distinct; + to._selectExpression = from._selectExpression; + to._whereCascadeExpression = new List(from._whereCascadeExpression.ToArray()); + to._whereGlobalFilter = new List(from._whereGlobalFilter.ToArray()); } + } + public abstract partial class Select0Provider : Select0Provider, ISelect0 where TSelect : class where T1 : class + { public Select0Provider(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) { _orm = orm; @@ -1082,7 +1085,7 @@ namespace FreeSql.Internal.CommonProvider 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(); - protected ISelectGrouping InternalGroupBy(Expression columns) + public ISelectGrouping InternalGroupBy(Expression columns) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); @@ -1093,7 +1096,7 @@ namespace FreeSql.Internal.CommonProvider this.GroupBy(sql.Length > 0 ? sql.Substring(2) : null); return new SelectGroupingProvider(_orm, this, map, sql, _commonExpression, _tables); } - protected TSelect InternalJoin(Expression exp, SelectTableInfoType joinType) + public TSelect InternalJoin(Expression exp, SelectTableInfoType joinType) { _commonExpression.ExpressionJoinLambda(_tables, joinType, exp, null, _whereCascadeExpression); return this as TSelect; @@ -1109,7 +1112,7 @@ namespace FreeSql.Internal.CommonProvider 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"); - protected List InternalToList(Expression select) => this.ToListMapReader(this.GetExpressionField(select)); + 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); @@ -1151,7 +1154,7 @@ namespace FreeSql.Internal.CommonProvider return this.ToListMapReader(new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null)).FirstOrDefault(); } - protected TSelect InternalWhere(Expression exp) => exp == null ? this as TSelect : this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp, null, _whereCascadeExpression, _params)); + public TSelect InternalWhere(Expression exp) => exp == null ? this as TSelect : this.Where(_commonExpression.ExpressionWhereLambda(_tables, exp, null, _whereCascadeExpression, _params)); #endregion #if net40 diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index f47cf885..50b62e21 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -111,15 +111,15 @@ namespace FreeSql.Internal.CommonProvider return this.InternalAvg(column?.Body); } - public abstract ISelect From(Expression, T2, ISelectFromExpression>> exp) where T2 : class;// { this.InternalFrom(exp); var ret = new Select3Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) where T2 : class where T3 : class;// { this.InternalFrom(exp); var ret = new Select3Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class;// { this.InternalFrom(exp); var ret = new Select4Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class;// { this.InternalFrom(exp); var ret = new Select5Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class;// { this.InternalFrom(exp); var ret = new Select6Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class;// { this.InternalFrom(exp); var ret = new Select7Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class;// { this.InternalFrom(exp); var ret = new Select8Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class;// { this.InternalFrom(exp); var ret = new Select9Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class;// { this.InternalFrom(exp); var ret = new Select10Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } + public abstract ISelect From(Expression, T2, ISelectFromExpression>> exp) where T2 : class; + public abstract ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) where T2 : class where T3 : class; + public abstract ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class; + public abstract ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class; + public abstract ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class; + public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class; + public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class; + public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class; + public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class; public ISelectGrouping GroupBy(Expression> columns) { @@ -132,41 +132,48 @@ namespace FreeSql.Internal.CommonProvider { if (column == null) return default(TMember); _tables[0].Parameter = column.Parameters[0]; - return this.InternalMax(column?.Body); + return this.InternalMax(column.Body); } public TMember Min(Expression> column) { if (column == null) return default(TMember); _tables[0].Parameter = column.Parameters[0]; - return this.InternalMin(column?.Body); + return this.InternalMin(column.Body); + } + public void OrderByReflection(LambdaExpression column, bool isDescending) + { + if (column == null) return; + _tables[0].Parameter = column.Parameters[0]; + if (isDescending) this.InternalOrderByDescending(column.Body); + else this.InternalOrderBy(column.Body); } public ISelect OrderBy(Expression> column) => this.OrderBy(true, column); public ISelect OrderBy(bool condition, Expression> column) { if (condition == false || column == null) return this; _tables[0].Parameter = column.Parameters[0]; - return this.InternalOrderBy(column?.Body); + return this.InternalOrderBy(column.Body); } public ISelect OrderByDescending(Expression> column) => this.OrderByDescending(true, column); public ISelect OrderByDescending(bool condition, Expression> column) { if (condition == false || column == null) return this; _tables[0].Parameter = column.Parameters[0]; - return this.InternalOrderByDescending(column?.Body); + return this.InternalOrderByDescending(column.Body); } public decimal Sum(Expression> column) { if (column == null) return default(decimal); _tables[0].Parameter = column.Parameters[0]; - return this.InternalSum(column?.Body); + return this.InternalSum(column.Body); } public List ToList(Expression> select) { if (select == null) return this.InternalToList(select?.Body); _tables[0].Parameter = select.Parameters[0]; - return this.InternalToList(select?.Body); + return this.InternalToList(select.Body); } public List ToList() => ToList(GetToListDtoSelector()); @@ -177,89 +184,6 @@ namespace FreeSql.Internal.CommonProvider _tables[0].Parameter ?? Expression.Parameter(typeof(T1), "a")); } - #region linq to sql - public ISelect Select(Expression> select) where TReturn : class - { - if (typeof(TReturn) == typeof(T1)) return this as ISelect; - _tables[0].Parameter = select.Parameters[0]; - _selectExpression = select.Body; - if (_orm.CodeFirst.IsAutoSyncStructure) - (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TReturn)); //._dicSyced.TryAdd(typeof(TReturn), true); - var ret = _orm.Select(); - Select0Provider, T1>.CopyData(this, ret, null); - return ret; - } - public ISelect Join(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression> resultSelector) where TInner : class where TResult : class - { - _tables[0].Parameter = resultSelector.Parameters[0]; - _commonExpression.ExpressionLambdaToSql(outerKeySelector, new CommonExpression.ExpTSC { _tables = _tables }); - this.InternalJoin(Expression.Lambda>( - Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), - new[] { outerKeySelector.Parameters[0], innerKeySelector.Parameters[0] } - ), SelectTableInfoType.InnerJoin); - if (typeof(TResult) == typeof(T1)) return this as ISelect; - _selectExpression = resultSelector.Body; - if (_orm.CodeFirst.IsAutoSyncStructure) - (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TResult)); //._dicSyced.TryAdd(typeof(TResult), true); - var ret = _orm.Select() as Select1Provider; - Select0Provider, T1>.CopyData(this, ret, null); - return ret; - } - public ISelect GroupJoin(ISelect inner, Expression> outerKeySelector, Expression> innerKeySelector, Expression, TResult>> resultSelector) where TInner : class where TResult : class - { - _tables[0].Parameter = resultSelector.Parameters[0]; - _commonExpression.ExpressionLambdaToSql(outerKeySelector, new CommonExpression.ExpTSC { _tables = _tables }); - this.InternalJoin(Expression.Lambda>( - Expression.Equal(outerKeySelector.Body, innerKeySelector.Body), - new[] { outerKeySelector.Parameters[0], innerKeySelector.Parameters[0] } - ), SelectTableInfoType.InnerJoin); - if (typeof(TResult) == typeof(T1)) return this as ISelect; - _selectExpression = resultSelector.Body; - if (_orm.CodeFirst.IsAutoSyncStructure) - (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TResult)); //._dicSyced.TryAdd(typeof(TResult), true); - var ret = _orm.Select() as Select1Provider; - Select0Provider, T1>.CopyData(this, ret, null); - return ret; - } - public ISelect SelectMany(Expression>> collectionSelector, Expression> resultSelector) where TCollection : class where TResult : class - { - SelectTableInfo find = null; - if (collectionSelector.Body.NodeType == ExpressionType.Call) - { - var callExp = collectionSelector.Body as MethodCallExpression; - if (callExp.Method.Name == "DefaultIfEmpty" && callExp.Object.Type.GetGenericArguments().Any()) - { - find = _tables.Where((a, idx) => idx > 0 && a.Type == SelectTableInfoType.InnerJoin && a.Table.Type == callExp.Object.Type.GetGenericArguments()[0]).LastOrDefault(); - if (find != null) - { - if (!string.IsNullOrEmpty(find.On)) find.On = Regex.Replace(find.On, $@"\b{find.Alias}\.", $"{resultSelector.Parameters[1].Name}."); - if (!string.IsNullOrEmpty(find.NavigateCondition)) find.NavigateCondition = Regex.Replace(find.NavigateCondition, $@"\b{find.Alias}\.", $"{resultSelector.Parameters[1].Name}."); - find.Type = SelectTableInfoType.LeftJoin; - find.Alias = resultSelector.Parameters[1].Name; - find.Parameter = resultSelector.Parameters[1]; - } - } - } - if (find == null) - { - var tb = _commonUtils.GetTableByEntity(typeof(TCollection)); - if (tb == null) throw new Exception($"SelectMany 错误的类型:{typeof(TCollection).FullName}"); - _tables.Add(new SelectTableInfo { Alias = resultSelector.Parameters[1].Name, AliasInit = resultSelector.Parameters[1].Name, Parameter = resultSelector.Parameters[1], Table = tb, Type = SelectTableInfoType.From }); - } - if (typeof(TResult) == typeof(T1)) return this as ISelect; - _selectExpression = resultSelector.Body; - if (_orm.CodeFirst.IsAutoSyncStructure) - (_orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(TResult)); //._dicSyced.TryAdd(typeof(TResult), true); - var ret = _orm.Select() as Select1Provider; - Select0Provider, T1>.CopyData(this, ret, null); - return ret; - } - public ISelect DefaultIfEmpty() - { - return this; - } - #endregion - public DataTable ToDataTable(Expression> select) { if (select == null) return this.InternalToDataTable(select?.Body); @@ -1083,8 +1007,6 @@ namespace FreeSql.Internal.CommonProvider _trackToList?.Invoke(list); } - public IQueryable AsQueryable() => new QueryableProvider(this); - #if net40 #else async internal Task SetListAsync(IEnumerable list) diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs index 8c2474c4..e353c0f5 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs @@ -1,5 +1,6 @@ using FreeSql.Internal.Model; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -9,14 +10,15 @@ using System.Threading.Tasks; namespace FreeSql.Internal.CommonProvider { - public class SelectGroupingProvider : ISelectGrouping + public class SelectGroupingProvider { - internal IFreeSql _orm; - internal object _select; - internal ReadAnonymousTypeInfo _map; - internal string _field; - internal CommonExpression _comonExp; - internal List _tables; + public IFreeSql _orm; + public object _select; + public ReadAnonymousTypeInfo _map; + public string _field; + public CommonExpression _comonExp; + public List _tables; + public SelectGroupingProvider(IFreeSql orm, object select, ReadAnonymousTypeInfo map, string field, CommonExpression comonExp, List tables) { _orm = orm; @@ -27,7 +29,7 @@ namespace FreeSql.Internal.CommonProvider _tables = tables; } - string getSelectGroupingMapString(Expression[] members) + public string getSelectGroupingMapString(Expression[] members) { if (members.Any() == false) return _map.DbField; var parentName = ((members.FirstOrDefault() as MemberExpression)?.Expression as MemberExpression)?.Member.Name; @@ -85,59 +87,45 @@ namespace FreeSql.Internal.CommonProvider return null; } - public ISelectGrouping Having(Expression, bool>> exp) + public void InternalHaving(Expression exp) { var sql = _comonExp.ExpressionWhereLambda(null, exp, getSelectGroupingMapString, null, null); var method = _select.GetType().GetMethod("Having", new[] { typeof(string), typeof(object) }); method.Invoke(_select, new object[] { sql, null }); - return this; } - - public ISelectGrouping OrderBy(Expression, TMember>> column) + public void InternalOrderBy(Expression exp, bool isDescending) { - var sql = _comonExp.ExpressionWhereLambda(null, column, getSelectGroupingMapString, null, null); + var sql = _comonExp.ExpressionWhereLambda(null, exp, getSelectGroupingMapString, null, null); var method = _select.GetType().GetMethod("OrderBy", new[] { typeof(string), typeof(object) }); - method.Invoke(_select, new object[] { sql, null }); - return this; + method.Invoke(_select, new object[] { isDescending ? $"{sql} DESC" : sql, null }); } - - public ISelectGrouping OrderByDescending(Expression, TMember>> column) - { - var sql = _comonExp.ExpressionWhereLambda(null, column, getSelectGroupingMapString, null, null); - var method = _select.GetType().GetMethod("OrderBy", new[] { typeof(string), typeof(object) }); - method.Invoke(_select, new object[] { $"{sql} DESC", null }); - return this; - } - - public List Select(Expression, TReturn>> select) => ToList(select); - public List ToList(Expression, TReturn>> select) + public object InternalToList(Expression select, Type elementType, bool isAsync) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _comonExp.ReadAnonymousField(null, field, map, ref index, select, getSelectGroupingMapString, null, false); - if (map.Childs.Any() == false && map.MapType == null) map.MapType = typeof(TReturn); - var method = _select.GetType().GetMethod("ToListMapReader", BindingFlags.Instance | BindingFlags.NonPublic); - method = method.MakeGenericMethod(typeof(TReturn)); - return method.Invoke(_select, new object[] { new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null) }) as List; + if (map.Childs.Any() == false && map.MapType == null) map.MapType = elementType; + var method = _select.GetType().GetMethod(isAsync ? "ToListMapReaderAsync" : "ToListMapReader", BindingFlags.Instance | BindingFlags.NonPublic); + method = method.MakeGenericMethod(elementType); + return method.Invoke(_select, new object[] { new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null) }); } - public Dictionary ToDictionary(Expression, TElement>> elementSelector) + public IEnumerable> InternalToKeyValuePairs(Expression elementSelector, Type elementType) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); var index = 0; _comonExp.ReadAnonymousField(null, field, map, ref index, elementSelector, getSelectGroupingMapString, null, false); - if (map.Childs.Any() == false && map.MapType == null) map.MapType = typeof(TElement); + if (map.Childs.Any() == false && map.MapType == null) map.MapType = elementType; var method = _select.GetType().GetMethod("ToListMapReaderPrivate", BindingFlags.Instance | BindingFlags.NonPublic); - method = method.MakeGenericMethod(typeof(TElement)); + method = method.MakeGenericMethod(elementType); var otherAf = new ReadAnonymousTypeOtherInfo(_field, _map, new List()); - var values = method.Invoke(_select, new object[] { new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null), new[] { otherAf } }) as List; - return otherAf.retlist.Select((a, b) => new KeyValuePair((TKey)a, values[b])).ToDictionary(a => a.Key, a => a.Value); + var values = method.Invoke(_select, new object[] { new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null), new[] { otherAf } }) as IList; + return otherAf.retlist.Select((a, b) => new KeyValuePair(a, values[b])); } - - public string ToSql(Expression, TReturn>> select, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) + public string InternalToSql(Expression select, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) { var map = new ReadAnonymousTypeInfo(); var field = new StringBuilder(); @@ -147,6 +135,14 @@ namespace FreeSql.Internal.CommonProvider var method = _select.GetType().GetMethod("ToSql", new[] { typeof(string) }); return method.Invoke(_select, new object[] { field.Length > 0 ? field.Remove(0, 2).ToString() : null }) as string; } + } + + public class SelectGroupingProvider : SelectGroupingProvider, ISelectGrouping + { + public SelectGroupingProvider(IFreeSql orm, object select, ReadAnonymousTypeInfo map, string field, CommonExpression comonExp, List tables) + :base(orm, select, map, field, comonExp, tables) { } + + public string ToSql(Expression, TReturn>> select, FieldAliasOptions fieldAlias = FieldAliasOptions.AsIndex) => InternalToSql(select, fieldAlias); public string ToSql(string field) { if (string.IsNullOrEmpty(field)) @@ -163,7 +159,6 @@ namespace FreeSql.Internal.CommonProvider return this; } public ISelectGrouping Offset(int offset) => this.Skip(offset); - public ISelectGrouping Limit(int limit) { var method = _select.GetType().GetMethod("Limit", new[] { typeof(int) }); @@ -171,7 +166,6 @@ namespace FreeSql.Internal.CommonProvider return this; } public ISelectGrouping Take(int limit) => this.Limit(limit); - public ISelectGrouping Page(int pageNumber, int pageSize) { var method = _select.GetType().GetMethod("Page", new[] { typeof(int), typeof(int) }); @@ -186,22 +180,31 @@ namespace FreeSql.Internal.CommonProvider return this; } + public ISelectGrouping Having(Expression, bool>> exp) + { + InternalHaving(exp); + return this; + } + public ISelectGrouping OrderBy(Expression, TMember>> column) + { + InternalOrderBy(column, false); + return this; + } + public ISelectGrouping OrderByDescending(Expression, TMember>> column) + { + InternalOrderBy(column, true); + return this; + } + + public List Select(Expression, TReturn>> select) => ToList(select); + public List ToList(Expression, TReturn>> select) => InternalToList(select, typeof(TReturn), false) as List; + public Dictionary ToDictionary(Expression, TElement>> elementSelector) => InternalToKeyValuePairs(elementSelector, typeof(TElement)).ToDictionary(a => (TKey)a.Key, a => (TElement)a.Value); + #if net40 #else async public Task CountAsync() => long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync($"select count(1) from ({this.ToSql($"1{_comonExp._common.FieldAsAlias("as1")}")}) fta")), out var trylng) ? trylng : default(long); - public Task> ToListAsync(Expression, TReturn>> select) - { - var map = new ReadAnonymousTypeInfo(); - var field = new StringBuilder(); - var index = 0; - - _comonExp.ReadAnonymousField(null, field, map, ref index, select, getSelectGroupingMapString, null, false); - if (map.Childs.Any() == false && map.MapType == null) map.MapType = typeof(TReturn); - var method = _select.GetType().GetMethod("ToListMapReaderAsync", BindingFlags.Instance | BindingFlags.NonPublic); - method = method.MakeGenericMethod(typeof(TReturn)); - return method.Invoke(_select, new object[] { new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null) }) as Task>; - } + public Task> ToListAsync(Expression, TReturn>> select) => InternalToList(select, typeof(TReturn), true) as Task>; async public Task> ToDictionaryAsync(Expression, TElement>> elementSelector) { var map = new ReadAnonymousTypeInfo();