diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml
index 19871dd2..f5004a0a 100644
--- a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml
+++ b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.xml
@@ -7,11 +7,21 @@
将 ISelect<T1> 转换为 IQueryable<T1>
- 此方法主要用于扩展,比如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象
- 注意:IQueryable 方法污染较为严重,请尽量避免此转换
+ 用于扩展如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象
+ 提示:IQueryable 方法污染严重,查询功能的实现也不理想,应尽量避免此转换
+ IQueryable<T1> 扩展方法 RestoreToSelect() 可以还原为 ISelect<T1>
+
+
+ 将 IQueryable<T1> 转换为 ISelect<T1>
+ 前提:IQueryable 必须由 FreeSql.Extensions.Linq.QueryableProvider 实现
+
+
+
+
+
【linq to sql】专用扩展方法,不建议直接使用
diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs b/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs
index de256cec..5df25a29 100644
--- a/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs
+++ b/Extensions/FreeSql.Extensions.Linq/FreeSqlExtensionsLinq.cs
@@ -15,14 +15,27 @@ public static class FreeSqlExtensionsLinqSql
///
/// 将 ISelect<T1> 转换为 IQueryable<T1>
- /// 此方法主要用于扩展,比如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象
- /// 注意:IQueryable 方法污染较为严重,请尽量避免此转换
+ /// 用于扩展如:abp IRepository GetAll() 接口方法需要返回 IQueryable 对象
+ /// 提示:IQueryable 方法污染严重,查询功能的实现也不理想,应尽量避免此转换
+ /// IQueryable<T1> 扩展方法 RestoreToSelect() 可以还原为 ISelect<T1>
///
///
public static IQueryable AsQueryable(this ISelect that) where T1 : class
{
return new QueryableProvider(that as Select1Provider);
}
+ ///
+ /// 将 IQueryable<T1> 转换为 ISelect<T1>
+ /// 前提:IQueryable 必须由 FreeSql.Extensions.Linq.QueryableProvider 实现
+ ///
+ ///
+ ///
+ ///
+ public static ISelect RestoreToSelect(this IQueryable that) where T1 : class
+ {
+ var queryable = that as QueryableProvider ?? throw new Exception($"无法将 IQueryable<{typeof(T1).Name}> 转换为 ISelect<{typeof(T1).Name}>,因为他的实现不是 FreeSql.Extensions.Linq.QueryableProvider");
+ return queryable._select;
+ }
///
/// 【linq to sql】专用扩展方法,不建议直接使用
diff --git a/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs b/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs
index b65147b1..35b2516c 100644
--- a/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs
+++ b/Extensions/FreeSql.Extensions.Linq/QueryableProvider.cs
@@ -13,15 +13,15 @@ namespace FreeSql.Extensions.Linq
{
class QueryableProvider : IQueryable, IOrderedQueryable where TSource : class
{
- private Expression _expression;
- private IQueryProvider _provider;
- private Select1Provider _select;
+ Expression _expression;
+ IQueryProvider _provider;
+ internal Select1Provider _select;
public QueryableProvider(Select1Provider select)
{
_select = select;
_expression = Expression.Constant(this);
- _provider = new QueryProvider(_select);
+ _provider = new QueryProvider(_select, _expression);
}
public QueryableProvider(Expression expression, IQueryProvider provider, Select1Provider select)
{
@@ -49,56 +49,58 @@ namespace FreeSql.Extensions.Linq
class QueryProvider : IQueryProvider where TSource : class
{
- private Select1Provider _select;
+ Select1Provider _select;
+ Expression _oldExpression;
- public QueryProvider(Select1Provider select)
+ public QueryProvider(Select1Provider select, Expression oldExpression)
{
_select = select;
+ _oldExpression = oldExpression;
}
public IQueryable CreateQuery(Expression expression)
{
+ ExecuteExp(expression, null, false);
if (typeof(TElement) != typeof(TCurrent))
- return new QueryableProvider(expression, new QueryProvider(_select), _select);
+ return new QueryableProvider(expression, new QueryProvider(_select, expression), _select);
+ _oldExpression = expression;
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;
- }
+ return (TResult)ExecuteExp(expression, typeof(TResult), _oldExpression == expression);
+ }
+ public object Execute(Expression expression) => throw new NotImplementedException();
- SelectGroupingProvider groupBy = null;
+ public object ExecuteExp(Expression expression, Type tresult, bool isProcessed)
+ {
+ var callExp = expression as MethodCallExpression;
var isfirst = false;
- while (stackCallExps.Any())
+ if (callExp != null && isProcessed == false)
{
- callExp = stackCallExps.Pop();
- TResult throwCallExp(string message) => throw new Exception($"FreeSql Queryable 解析出错,执行的方法 {callExp.Method.Name} {message}");
+ object throwCallExp(string message) => throw new Exception($"解析失败 {callExp.Method.Name} {message},提示:可以使用扩展方法 IQueryable.RestoreToSelect() 还原为 ISelect 再查询");
if (callExp.Method.DeclaringType != typeof(Queryable)) return throwCallExp($"必须属于 System.Linq.Queryable");
- TResult tplMaxMinAvgSum(string method) {
+ object tplMaxMinAvgSum(string method)
+ {
if (callExp.Arguments.Count == 2)
{
var avgParam = (callExp.Arguments[1] as UnaryExpression)?.Operand as LambdaExpression;
- return (TResult)Utils.GetDataReaderValue(typeof(TResult),
+ return Utils.GetDataReaderValue(tresult,
_select.GetType().GetMethod(method).MakeGenericMethod(avgParam.ReturnType).Invoke(_select, new object[] { avgParam }));
}
return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法");
}
- TResult tplOrderBy(string method, bool isDescending)
+ object 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 tresult.CreateInstanceGetDefaultValue();
}
return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法");
}
@@ -106,7 +108,7 @@ namespace FreeSql.Extensions.Linq
{
case "Any":
if (callExp.Arguments.Count == 2) _select.InternalWhere(callExp.Arguments[1]);
- return (TResult)(object)_select.Any();
+ return _select.Any();
case "AsQueryable":
break;
@@ -123,13 +125,13 @@ namespace FreeSql.Extensions.Linq
var dywhere = callExp.Arguments[1].GetConstExprValue();
if (dywhere == null) return throwCallExp($" 参数值不能为 null");
_select.WhereDynamic(dywhere);
- return (TResult)(object)_select.Any();
+ return _select.Any();
}
return throwCallExp($" 不支持 {callExp.Arguments.Count}个参数的方法");
case "Count":
if (callExp.Arguments.Count == 2) _select.InternalWhere(callExp.Arguments[1]);
- return (TResult)Utils.GetDataReaderValue(typeof(TResult), _select.Count());
-
+ return Utils.GetDataReaderValue(tresult, _select.Count());
+
case "Distinct":
if (callExp.Arguments.Count == 1)
{
@@ -153,25 +155,24 @@ namespace FreeSql.Extensions.Linq
isfirst = true;
break;
- case "OrderBy":
+ case "OrderBy":
tplOrderBy("OrderByReflection", false);
break;
- case "OrderByDescending":
- tplOrderBy("OrderByReflection", true);
+ case "OrderByDescending":
+ tplOrderBy("OrderByReflection", true);
break;
- case "ThenBy":
- tplOrderBy("OrderByReflection", false);
+ case "ThenBy":
+ tplOrderBy("OrderByReflection", false);
break;
- case "ThenByDescending":
- tplOrderBy("OrderByReflection", true);
+ 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);
+ _select.InternalWhere(whereParam);
break;
}
return throwCallExp(" 不支持");
@@ -185,7 +186,7 @@ namespace FreeSql.Extensions.Linq
case "ToList":
if (callExp.Arguments.Count == 1)
- return (TResult)(object)_select.ToList();
+ return _select.ToList();
return throwCallExp(" 不支持");
case "Select":
@@ -242,37 +243,21 @@ namespace FreeSql.Extensions.Linq
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 (tresult == null) return null;
if (isfirst)
{
_select.Limit(1);
if (_select._selectExpression != null)
- return (TResult)(object)_select.InternalToList(_select._selectExpression).FirstOrDefault();
- return (TResult)(object)_select.ToList().FirstOrDefault();
+ return _select.InternalToList(_select._selectExpression).FirstOrDefault();
+ return _select.ToList().FirstOrDefault();
}
if (_select._selectExpression != null)
- return (TResult)(object)_select.InternalToList(_select._selectExpression);
- return (TResult)(object)_select.ToList();
+ return _select.InternalToList(_select._selectExpression);
+ return _select.ToList();
}
- public object Execute(Expression expression) => throw new NotImplementedException();
}
}
diff --git a/FreeSql.Tests/FreeSql.Tests/Queryable/ExprHelperTest.cs b/FreeSql.Tests/FreeSql.Tests/Linq/ExprHelperTest.cs
similarity index 100%
rename from FreeSql.Tests/FreeSql.Tests/Queryable/ExprHelperTest.cs
rename to FreeSql.Tests/FreeSql.Tests/Linq/ExprHelperTest.cs
diff --git a/FreeSql.Tests/FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs b/FreeSql.Tests/FreeSql.Tests/Linq/ISelectLinqToSqlTests.cs
similarity index 99%
rename from FreeSql.Tests/FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs
rename to FreeSql.Tests/FreeSql.Tests/Linq/ISelectLinqToSqlTests.cs
index 555d7c83..584b94c3 100644
--- a/FreeSql.Tests/FreeSql.Tests/LinqToSql/SqliteLinqToSqlTests.cs
+++ b/FreeSql.Tests/FreeSql.Tests/Linq/ISelectLinqToSqlTests.cs
@@ -3,10 +3,8 @@ using System;
using System.Linq;
using Xunit;
-namespace FreeSql.Tests.LinqToSql
+namespace FreeSql.Tests.Linq
{
-
-
class TestLinqToSql
{
public Guid id { get; set; }
@@ -29,7 +27,7 @@ namespace FreeSql.Tests.LinqToSql
public DateTime createtime { get; set; } = DateTime.Now;
}
- public class SqliteLinqToSqlTests
+ public class ISelectLinqToSqlTests
{
[Fact]
diff --git a/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs b/FreeSql.Tests/FreeSql.Tests/Linq/QueryableLinqToSqlTests.cs
similarity index 88%
rename from FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs
rename to FreeSql.Tests/FreeSql.Tests/Linq/QueryableLinqToSqlTests.cs
index f028f93a..6d234d0b 100644
--- a/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableLinqToSqlTests.cs
+++ b/FreeSql.Tests/FreeSql.Tests/Linq/QueryableLinqToSqlTests.cs
@@ -58,29 +58,6 @@ namespace FreeSql.Tests.Linq
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()
{
diff --git a/FreeSql.Tests/FreeSql.Tests/Linq/QueryableRestoreToSelectTest.cs b/FreeSql.Tests/FreeSql.Tests/Linq/QueryableRestoreToSelectTest.cs
new file mode 100644
index 00000000..6bc941f9
--- /dev/null
+++ b/FreeSql.Tests/FreeSql.Tests/Linq/QueryableRestoreToSelectTest.cs
@@ -0,0 +1,50 @@
+using FreeSql.DataAnnotations;
+using FreeSql;
+using System;
+using System.Collections.Generic;
+using Xunit;
+using System.Linq;
+using Newtonsoft.Json.Linq;
+using NpgsqlTypes;
+using Npgsql.LegacyPostgis;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using System.ComponentModel.DataAnnotations;
+using System.Threading;
+using System.Data.SqlClient;
+using kwlib;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+
+namespace FreeSql.Tests.Linq
+{
+ public class QueryableRestoreToSelectTest
+ {
+ class qt01
+ {
+ [Column(IsIdentity = true)]
+ public int id { get; set; }
+ public string name { get; set; }
+
+ [Navigate(nameof(qt01_item.qt01id))]
+ public List items { get; set; }
+ }
+ class qt01_item
+ {
+ [Column(IsIdentity = true)]
+ public int id { get; set; }
+ public string title { get; set; }
+ public int qt01id { get; set; }
+ }
+ IFreeSql fsql => g.sqlite;
+
+ [Fact]
+ public void RestoreToSelect()
+ {
+ Assert.Equal(fsql.Select().Skip(2).First(a => a.name), fsql.Select().AsQueryable().Skip(2).Take(1).RestoreToSelect().First(a => a.name));
+ Assert.Equal(fsql.Select().Skip(2).First(a => new { a.name }).name, fsql.Select().AsQueryable().Skip(2).Take(1).RestoreToSelect().First(a => new { a.name }).name);
+ }
+ }
+
+}
diff --git a/FreeSql.Tests/FreeSql.Tests/Queryable/QueryableTest.cs b/FreeSql.Tests/FreeSql.Tests/Linq/QueryableTest.cs
similarity index 100%
rename from FreeSql.Tests/FreeSql.Tests/Queryable/QueryableTest.cs
rename to FreeSql.Tests/FreeSql.Tests/Linq/QueryableTest.cs
diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
index 45ce906d..c787b5eb 100644
--- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
+++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs
@@ -214,7 +214,7 @@ public static partial class FreeSqlGlobalExtensions
}
///
- /// 将 IEnumable<T> 转成 ISelect<T>,以便使用 FreeSql 的查询功能。此方法用于 Lambad 表达式中,快速进行集合导航的查询。
+ /// 将 IEnumable<T> 转成 ISelect<T>,以便使用 FreeSql 的查询功能。此方法用于 Lambda 表达式中,快速进行集合导航的查询。
///
///
///
diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml
index 12b3cc3e..18542e5f 100644
--- a/FreeSql/FreeSql.xml
+++ b/FreeSql/FreeSql.xml
@@ -2281,6 +2281,137 @@
+
+
+ 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】
+
+
+
+
+
+
+
+
+ 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > ?age", new { age = 25 })
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
+ 查询,ExecuteArrayAsync("select * from user where age > ?age", new { age = 25 })
+
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
+ 查询,ExecuteDataSetAsync("select * from user where age > ?age; select 2", new { age = 25 })
+
+
+
+
+
+
+
+ 查询
+
+
+
+
+
+
+ 查询,ExecuteDataTableAsync("select * from user where age > ?age", new { age = 25 })
+
+
+
+
+
+
+
+ 在【主库】执行
+
+
+
+
+
+
+
+ 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > ?age", new { age = 25 })
+
+
+
+
+
+
+
+ 在【主库】执行
+
+
+
+
+
+
+
+ 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > ?age", new { age = 25 })
+
+
+
+
+
+
+
+ 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > ?age", new SqlParameter { ParameterName = "age", Value = 25 })
+
+
+
+
+
+
+
+
+
+ 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > ?age", new { age = 25 })
+
+
+
+
+
+
+
+
+ 执行SQL返回对象集合,Query<User>("select * from user where age > ?age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 })
+
+
+
+
+
+
+
+
+
+ 执行SQL返回对象集合,Query<User>("select * from user where age > ?age; select * from address", new { age = 25 })
+
+
+
+
+
+
可自定义解析表达式
@@ -2801,6 +2932,12 @@
超时
+
+
+ 获取资源
+
+
+
使用完毕后,归还资源
@@ -2871,6 +3008,12 @@
资源对象
+
+
+ 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象
+
+ 资源对象
+
归还对象给对象池的时候触发
@@ -3092,7 +3235,7 @@
- 将 IEnumable<T> 转成 ISelect<T>,以便使用 FreeSql 的查询功能。此方法用于 Lambad 表达式中,快速进行集合导航的查询。
+ 将 IEnumable<T> 转成 ISelect<T>,以便使用 FreeSql 的查询功能。此方法用于 Lambda 表达式中,快速进行集合导航的查询。
@@ -3503,167 +3646,4 @@
-
-
-
-
-
- 使用 or 拼接两个 lambda 表达式
-
-
-
-
-
- 使用 or 拼接两个 lambda 表达式
-
-
- true 时生效
-
-
-
-
-
- 将 lambda 表达式取反
-
-
- true 时生效
-
-
-
-
- 生成类似Mongodb的ObjectId有序、不重复Guid
-
-
-
-
-
- 插入数据
-
-
-
-
-
-
- 插入数据,传入实体
-
-
-
-
-
-
-
- 插入数据,传入实体数组
-
-
-
-
-
-
-
- 插入数据,传入实体集合
-
-
-
-
-
-
-
- 插入数据,传入实体集合
-
-
-
-
-
-
-
- 修改数据
-
-
-
-
-
-
- 修改数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}
-
-
- 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合
-
-
-
-
- 查询数据
-
-
-
-
-
-
- 查询数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}
-
-
- 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合
-
-
-
-
- 删除数据
-
-
-
-
-
-
- 删除数据,传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}
-
-
- 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合
-
-
-
-
- 开启事务(不支持异步),60秒未执行完成(可能)被其他线程事务自动提交
-
- 事务体 () => {}
-
-
-
- 开启事务(不支持异步)
-
- 超时,未执行完成(可能)被其他线程事务自动提交
- 事务体 () => {}
-
-
-
- 开启事务(不支持异步)
-
-
- 事务体 () => {}
- 超时,未执行完成(可能)被其他线程事务自动提交
-
-
-
- 数据库访问对象
-
-
-
-
- 所有拦截方法都在这里
-
-
-
-
- CodeFirst 模式开发相关方法
-
-
-
-
- DbFirst 模式开发相关方法
-
-
-
-
- 全局过滤设置,可默认附加为 Select/Update/Delete 条件
-
-
-