diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 2d6d3409..b54d4d0e 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -509,14 +509,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 751d06c0..b25afc15 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1160,6 +1160,15 @@ + + + MySql find_in_set(str, strlist) + + + + + + PostgreSQL string_agg(.., ..) @@ -1639,6 +1648,17 @@ 使用属性名作为字段别名 + + + 控制取消本次查询 + * 不会产生额外的异常 + * 取消成功,则不执行 SQL 命令 + * 取消成功,直接返回没有记录时候的返回值 + * 取消成功,如 List<T> 返回 0 元素列表,不是 null,仍然是旧机制 + + 返回 true,则不会执行 SQL 命令 + + 指定事务对象 @@ -2331,6 +2351,15 @@ 选择一个导航属性 + + + 贪婪加载导航属性,如果查询中已经使用了 a.Parent.Parent 类似表达式,则可以无需此操作 + + + true 时生效 + 选择一个导航属性 + + 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 @@ -2352,6 +2381,14 @@ + + + 按属性名字符串进行 Include/IncludeMany 操作 + + true 时生效 + + + 实现 select .. from ( select ... from t ) a 这样的功能 diff --git a/FreeSql/Interface/Curd/ISelect/ISelect0.cs b/FreeSql/Interface/Curd/ISelect/ISelect0.cs index 11a54853..3f0119fc 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect0.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect0.cs @@ -30,6 +30,17 @@ namespace FreeSql Task CountAsync(CancellationToken cancellationToken = default); #endif + /// + /// 控制取消本次查询 + /// * 不会产生额外的异常 + /// * 取消成功,则不执行 SQL 命令 + /// * 取消成功,直接返回没有记录时候的返回值 + /// * 取消成功,如 List<T> 返回 0 元素列表,不是 null,仍然是旧机制 + /// + /// 返回 true,则不会执行 SQL 命令 + /// + TSelect Cancel(Func cancel); + /// /// 指定事务对象 /// diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index 74372279..4bff2524 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -321,6 +321,14 @@ namespace FreeSql /// ISelect Include(Expression> navigateSelector) where TNavigate : class; /// + /// 贪婪加载导航属性,如果查询中已经使用了 a.Parent.Parent 类似表达式,则可以无需此操作 + /// + /// + /// true 时生效 + /// 选择一个导航属性 + /// + ISelect IncludeIf(bool condition, Expression> navigateSelector) where TNavigate : class; + /// /// 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 /// 文档:https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd#%E5%AF%BC%E8%88%AA%E5%B1%9E%E6%80%A7-onetomanymanytomany /// @@ -340,6 +348,13 @@ namespace FreeSql /// /// ISelect IncludeByPropertyName(string property); + /// + /// 按属性名字符串进行 Include/IncludeMany 操作 + /// + /// true 时生效 + /// + /// + ISelect IncludeByPropertyNameIf(bool condition, string property); /// /// 实现 select .. from ( select ... from t ) a 这样的功能 diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index ea4c9606..0e69ee7f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -42,6 +42,7 @@ namespace FreeSql.Internal.CommonProvider public bool _distinct; public Expression _selectExpression; public List _whereGlobalFilter; + public Func _cancel; int _disposeCounter; ~Select0Provider() @@ -61,6 +62,7 @@ namespace FreeSql.Internal.CommonProvider _includeInfo.Clear(); _selectExpression = null; _whereGlobalFilter?.Clear(); + _cancel = null; } public static void CopyData(Select0Provider from, Select0Provider to, ReadOnlyCollection lambParms) @@ -119,6 +121,7 @@ namespace FreeSql.Internal.CommonProvider to._distinct = from._distinct; to._selectExpression = from._selectExpression; to._whereGlobalFilter = new List(from._whereGlobalFilter.ToArray()); + to._cancel = from._cancel; } public Expression ConvertStringPropertyToExpression(string property, bool fromFirstTable = false) @@ -183,6 +186,12 @@ namespace FreeSql.Internal.CommonProvider return this as TSelect; } + public TSelect Cancel(Func cancel) + { + _cancel = cancel; + return this as TSelect; + } + public TSelect WithTransaction(DbTransaction transaction) { _transaction = transaction; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs index 91c3f7c8..d4bf7c3d 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs @@ -21,11 +21,12 @@ namespace FreeSql.Internal.CommonProvider { public DataTable ToDataTable(string field = null) { + DataTable ret = null; + if (_cancel?.Invoke() == true) return ret; var sql = this.ToSql(field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - DataTable ret = null; Exception exception = null; try { @@ -46,12 +47,13 @@ namespace FreeSql.Internal.CommonProvider public List ToList(string field) { + var ret = new List(); + if (_cancel?.Invoke() == true) return ret; var sql = this.ToSql(field); var type = typeof(TTuple); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new List(); Exception exception = null; try { @@ -81,10 +83,11 @@ namespace FreeSql.Internal.CommonProvider } internal List ToListAfPrivate(string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { + var ret = new List(); + if (_cancel?.Invoke() == true) return ret; var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new List(); var retCount = 0; Exception exception = null; try @@ -133,6 +136,7 @@ namespace FreeSql.Internal.CommonProvider #region ToChunk internal void ToListAfChunkPrivate(int chunkSize, Action>> chunkDone, string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData) { + if (_cancel?.Invoke() == true) return; var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); @@ -208,6 +212,7 @@ namespace FreeSql.Internal.CommonProvider internal void ToListMrChunkPrivate(int chunkSize, Action>> chunkDone, string sql, ReadAnonymousTypeAfInfo af) { + if (_cancel?.Invoke() == true) return; var type = typeof(TReturn); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); @@ -266,12 +271,14 @@ namespace FreeSql.Internal.CommonProvider { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector)); + + var ret = new Dictionary(); + if (_cancel?.Invoke() == true) return ret; var af = this.GetAllFieldExpressionTreeLevel2(); var sql = this.ToSql(af.Field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new Dictionary(); Exception exception = null; try { @@ -297,11 +304,12 @@ namespace FreeSql.Internal.CommonProvider internal List ToListMrPrivate(string sql, ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData) { + var ret = new List(); + if (_cancel?.Invoke() == true) return ret; var type = typeof(TReturn); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new List(); var retCount = 0; Exception exception = null; try @@ -715,12 +723,13 @@ namespace FreeSql.Internal.CommonProvider } public int InternalInsertInto(string tableName, Expression select) { + int ret = 0; + if (_cancel?.Invoke() == true) return ret; var sql = this.InternalGetInsertIntoToSql(tableName, select); var dbParms = _params.ToArray(); var tb = _orm.CodeFirst.GetTableByEntity(typeof(TTargetEntity)); var before = new Aop.CurdBeforeEventArgs(tb.Type, tb, Aop.CurdType.Insert, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - int ret = 0; Exception exception = null; try { @@ -741,11 +750,12 @@ namespace FreeSql.Internal.CommonProvider protected DataTable InternalToDataTable(Expression select) { + DataTable ret = null; + if (_cancel?.Invoke() == true) return ret; var sql = this.InternalToSql(select, FieldAliasOptions.AsProperty); //DataTable 使用 AsProperty var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - DataTable ret = null; Exception exception = null; try { @@ -790,11 +800,12 @@ namespace FreeSql.Internal.CommonProvider #else async public Task ToDataTableAsync(string field, CancellationToken cancellationToken) { + DataTable ret = null; + if (_cancel?.Invoke() == true) return ret; var sql = this.ToSql(field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - DataTable ret = null; Exception exception = null; try { @@ -815,12 +826,13 @@ namespace FreeSql.Internal.CommonProvider async public Task> ToListAsync(string field, CancellationToken cancellationToken) { + var ret = new List(); + if (_cancel?.Invoke() == true) return ret; var sql = this.ToSql(field); var type = typeof(TTuple); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new List(); Exception exception = null; try { @@ -852,10 +864,11 @@ namespace FreeSql.Internal.CommonProvider async internal Task> ToListAfPrivateAsync(string sql, GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { + var ret = new List(); + if (_cancel?.Invoke() == true) return ret; var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new List(); var retCount = 0; Exception exception = null; try @@ -909,12 +922,14 @@ namespace FreeSql.Internal.CommonProvider { if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); if (elementSelector == null) throw new ArgumentNullException(nameof(elementSelector)); + + var ret = new Dictionary(); + if (_cancel?.Invoke() == true) return ret; var af = this.GetAllFieldExpressionTreeLevel2(); var sql = this.ToSql(af.Field); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new Dictionary(); Exception exception = null; try { @@ -941,11 +956,12 @@ namespace FreeSql.Internal.CommonProvider async internal Task> ToListMrPrivateAsync(string sql, ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { + var ret = new List(); + if (_cancel?.Invoke() == true) return ret; var type = typeof(TReturn); var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - var ret = new List(); var retCount = 0; Exception exception = null; try @@ -1006,12 +1022,13 @@ namespace FreeSql.Internal.CommonProvider async public Task InternalInsertIntoAsync(string tableName, Expression select, CancellationToken cancellationToken) { + int ret = 0; + if (_cancel?.Invoke() == true) return ret; var sql = this.InternalGetInsertIntoToSql(tableName, select); var dbParms = _params.ToArray(); var tb = _orm.CodeFirst.GetTableByEntity(typeof(TTargetEntity)); var before = new Aop.CurdBeforeEventArgs(tb.Type, tb, Aop.CurdType.Insert, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - int ret = 0; Exception exception = null; try { @@ -1032,11 +1049,12 @@ namespace FreeSql.Internal.CommonProvider async protected Task InternalToDataTableAsync(Expression select, CancellationToken cancellationToken) { + DataTable ret = null; + if (_cancel?.Invoke() == true) return ret; var sql = this.InternalToSql(select, FieldAliasOptions.AsProperty); //DataTable 使用 AsProperty var dbParms = _params.ToArray(); var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); - DataTable ret = null; Exception exception = null; try { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index c336fcf0..03360ada 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -393,6 +393,7 @@ namespace FreeSql.Internal.CommonProvider public int InsertInto(string tableName, Expression> select) where TTargetEntity : class => base.InternalInsertInto(tableName, select); + public ISelect IncludeByPropertyNameIf(bool condition, string property) => condition ? IncludeByPropertyName(property) : this; public ISelect IncludeByPropertyName(string property) { var exp = ConvertStringPropertyToExpression(property, true); @@ -424,6 +425,7 @@ namespace FreeSql.Internal.CommonProvider } bool _isIncluded = false; + public ISelect IncludeIf(bool condition, Expression> navigateSelector) where TNavigate : class => condition ? Include(navigateSelector) : this; public ISelect Include(Expression> navigateSelector) where TNavigate : class { var expBody = navigateSelector?.Body; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs index 44c1b28d..a25b713d 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs @@ -171,7 +171,7 @@ namespace FreeSql.Internal.CommonProvider return this; } - public long Count() => long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_select._connection, _select._transaction, CommandType.Text, $"select count(1) from ({this.ToSql($"1{_comonExp._common.FieldAsAlias("as1")}")}) fta", _select._commandTimeout, _select._params.ToArray())), out var trylng) ? trylng : default(long); + public long Count() => _select._cancel?.Invoke() == true ? 0 : long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_select._connection, _select._transaction, CommandType.Text, $"select count(1) from ({this.ToSql($"1{_comonExp._common.FieldAsAlias("as1")}")}) fta", _select._commandTimeout, _select._params.ToArray())), out var trylng) ? trylng : default(long); public ISelectGrouping Count(out long count) { count = this.Count(); @@ -200,7 +200,7 @@ namespace FreeSql.Internal.CommonProvider #if net40 #else - async public Task CountAsync(CancellationToken cancellationToken = default) => long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_select._connection, _select._transaction, CommandType.Text, $"select count(1) from ({this.ToSql($"1{_comonExp._common.FieldAsAlias("as1")}")}) fta", _select._commandTimeout, _select._params.ToArray(), cancellationToken)), out var trylng) ? trylng : default(long); + async public Task CountAsync(CancellationToken cancellationToken = default) => _select._cancel?.Invoke() == true ? 0 : long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_select._connection, _select._transaction, CommandType.Text, $"select count(1) from ({this.ToSql($"1{_comonExp._common.FieldAsAlias("as1")}")}) fta", _select._commandTimeout, _select._params.ToArray(), cancellationToken)), out var trylng) ? trylng : default(long); public Task> ToListAsync(Expression, TReturn>> select, CancellationToken cancellationToken = default) {