From 0ec4353e829f2175e0f22a05aa5c5f151cc0a546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=7E=7E=E7=A9=BA=E4=B8=80=7E=7E?= <407931232@qq.com> Date: Sun, 17 Nov 2024 20:23:00 +0800 Subject: [PATCH] =?UTF-8?q?DynamicFilterInfo=E6=96=B0=E5=A2=9EUseAllLogic?= =?UTF-8?q?=EF=BC=8C=E4=BD=BF=E7=94=A8=E6=89=80=E6=9C=89=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E8=BF=90=E7=AE=97=E7=AC=A6=E7=9A=84=E6=A8=A1=E5=BC=8F=EF=BC=8C?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=B8=BAFalse=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit False:按照原来的模式,只有分组的逻辑运算符会生成到查询条件中,只有OR分组会生成括号。 True:按照新的模式,所有的逻辑运算符都会生成到查询条件中,所有分组(AND或OR)都会生成括号。 WhereDynamicFilter(DynamicFilterInfo filter)中保留原有模式,并增加了对新模式的处理。 --- FreeSql.DbContext/FreeSql.DbContext.xml | 8 - FreeSql/FreeSql.xml | 269 ++++++++++-------- .../SelectProvider/Select0Provider.cs | 155 ++++++++-- FreeSql/Internal/Model/DynamicFilterInfo.cs | 27 +- 4 files changed, 305 insertions(+), 154 deletions(-) diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index c941a15d9..a7a94fcda 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -758,13 +758,5 @@ <param name="that"></param> <returns></returns> </member> - <member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Reflection.Assembly[])"> - <summary> - 批量注入 Repository,可以参考代码自行调整 - </summary> - <param name="services"></param> - <param name="assemblies"></param> - <returns></returns> - </member> </members> </doc> diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index fdde4800e..3805d19fa 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1087,6 +1087,93 @@ </summary> <returns></returns> </member> + <member name="T:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder"> + <summary> + 动态创建实体类型 + </summary> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.#ctor(IFreeSql,System.String,System.Attribute[])"> + <summary> + 配置Class + </summary> + <param name="className">类名</param> + <param name="attributes">类标记的特性[Table(Name = "xxx")] [Index(xxxx)]</param> + <returns></returns> + </member> + <member name="P:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.TypeBuilder"> + <summary> + 获取类型构建器,可作为要构建的Type来引用 + </summary> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.Property(System.String,System.Type,System.Attribute[])"> + <summary> + 配置属性 + </summary> + <param name="propertyName">属性名称</param> + <param name="propertyType">属性类型</param> + <param name="attributes">属性标记的特性-支持多个</param> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.Property(System.String,System.Type,System.Boolean,System.Attribute[])"> + <summary> + 配置属性 + </summary> + <param name="propertyName">属性名称</param> + <param name="propertyType">属性类型</param> + <param name="isOverride">该属性是否重写父类属性</param> + <param name="attributes">属性标记的特性-支持多个</param> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.Property(System.String,System.Type,System.Boolean,System.Object,System.Attribute[])"> + <summary> + 配置属性 + </summary> + <param name="propertyName">属性名称</param> + <param name="propertyType">属性类型</param> + <param name="isOverride">该属性是否重写父类属性</param> + <param name="defaultValue">属性默认值</param> + <param name="attributes">属性标记的特性-支持多个</param> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.Extend(System.Type)"> + <summary> + 配置父类 + </summary> + <param name="superClass">父类类型</param> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.OverrideProperty(System.Reflection.Emit.TypeBuilder@,System.Reflection.Emit.MethodBuilder,FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.PropertyMethodEnum,System.String)"> + <summary> + Override属性 + </summary> + <param name="typeBuilder"></param> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.Build"> + <summary> + Emit动态创建出Class - Type + </summary> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.BuildJustType"> + <summary> + Emit动态创建出Class - Type,不附带获取TableInfo + </summary> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.FirstCharToLower(System.String)"> + <summary> + 首字母小写 + </summary> + <param name="input"></param> + <returns></returns> + </member> + <member name="M:FreeSql.Extensions.DynamicEntity.DynamicCompileBuilder.FirstCharToUpper(System.String)"> + <summary> + 首字母大写 + </summary> + <param name="input"></param> + <returns></returns> + </member> <member name="M:FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetEntityKeyString(IFreeSql,System.Type,System.Object,System.Boolean,System.String)"> <summary> 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 "" @@ -4615,6 +4702,13 @@ 动态过滤条件 </summary> </member> + <member name="P:FreeSql.Internal.Model.DynamicFilterInfo.UseAllLogic"> + <summary> + 使用所有逻辑运算符,默认为False。 + False:按照原来的模式,只有分组的逻辑运算符会生成到查询条件中,只有OR分组会生成括号。 + True:按照新的模式,所有的逻辑运算符都会生成到查询条件中,所有分组(AND或OR)都会生成括号。 + </summary> + </member> <member name="P:FreeSql.Internal.Model.DynamicFilterInfo.Field"> <summary> 属性名:Name<para></para> @@ -5810,6 +5904,28 @@ 对象池 </summary> </member> + <member name="M:FreeSqlGlobalDynamicEntityExtensions.DynamicEntity(FreeSql.ICodeFirst,System.String,System.Attribute[])"> + <summary> + 动态构建Class Type + </summary> + <returns></returns> + </member> + <member name="M:FreeSqlGlobalDynamicEntityExtensions.CreateInstance(FreeSql.Internal.Model.TableInfo,System.Collections.Generic.Dictionary{System.String,System.Object})"> + <summary> + 根据字典,创建 table 对应的实体对象 + </summary> + <param name="table"></param> + <param name="dict"></param> + <returns></returns> + </member> + <member name="M:FreeSqlGlobalDynamicEntityExtensions.CreateDictionary(FreeSql.Internal.Model.TableInfo,System.Object)"> + <summary> + 根据实体对象,创建 table 对应的字典 + </summary> + <param name="table"></param> + <param name="instance"></param> + <returns></returns> + </member> <member name="M:FreeSqlGlobalExpressionCallExtensions.Between(System.DateTime,System.DateTime,System.DateTime)"> <summary> C#: that >= between && that <= and<para></para> @@ -5831,6 +5947,36 @@ <param name="end"></param> <returns></returns> </member> + <member name="M:FreeSqlGlobalExpressionCallExtensions.In``1(``0,``0)"> + <summary> + field IN (value1) + </summary> + </member> + <member name="M:FreeSqlGlobalExpressionCallExtensions.In``1(``0,``0,``0)"> + <summary> + field in (value1, value2) + </summary> + </member> + <member name="M:FreeSqlGlobalExpressionCallExtensions.In``1(``0,``0,``0,``0)"> + <summary> + field in (value1, value2, value3) + </summary> + </member> + <member name="M:FreeSqlGlobalExpressionCallExtensions.In``1(``0,``0,``0,``0,``0)"> + <summary> + field in (value1, value2, value3, value4) + </summary> + </member> + <member name="M:FreeSqlGlobalExpressionCallExtensions.In``1(``0,``0,``0,``0,``0,``0)"> + <summary> + field in (value1, value2, value3, value4, value5) + </summary> + </member> + <member name="M:FreeSqlGlobalExpressionCallExtensions.In``1(``0,``0[])"> + <summary> + field in (values) + </summary> + </member> <member name="M:FreeSqlGlobalExtensions.DisplayCsharp(System.Type,System.Boolean)"> <summary> 获取 Type 的原始 c# 文本表示 @@ -6346,126 +6492,3 @@ </member> </members> </doc> -<returns></returns> - </member> - <member name="M:IFreeSql.Insert``1(System.Collections.Generic.List{``0})"> - <summary> - 插入数据,传入实体集合 - </summary> - <typeparam name="T1"></typeparam> - <param name="source"></param> - <returns></returns> - </member> - <member name="M:IFreeSql.Insert``1(System.Collections.Generic.IEnumerable{``0})"> - <summary> - 插入数据,传入实体集合 - </summary> - <typeparam name="T1"></typeparam> - <param name="source"></param> - <returns></returns> - </member> - <member name="M:IFreeSql.InsertOrUpdate``1"> - <summary> - 插入或更新数据,此功能依赖数据库特性(低版本可能不支持),参考如下:<para></para> - MySql 5.6+: on duplicate key update<para></para> - PostgreSQL 9.4+: on conflict do update<para></para> - SqlServer 2008+: merge into<para></para> - Oracle 11+: merge into<para></para> - Sqlite: replace into<para></para> - DuckDB: on conflict do update<para></para> - 达梦: merge into<para></para> - 人大金仓:on conflict do update<para></para> - 神通:merge into<para></para> - MsAccess:不支持<para></para> - 注意区别:FreeSql.Repository 仓储也有 InsertOrUpdate 方法(不依赖数据库特性) - </summary> - <typeparam name="T1"></typeparam> - <returns></returns> - </member> - <member name="M:IFreeSql.Update``1"> - <summary> - 修改数据 - </summary> - <typeparam name="T1"></typeparam> - <returns></returns> - </member> - <member name="M:IFreeSql.Update``1(System.Object)"> - <summary> - 修改数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - </summary> - <typeparam name="T1"></typeparam> - <param name="dywhere">主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合</param> - <returns></returns> - </member> - <member name="M:IFreeSql.Select``1"> - <summary> - 查询数据 - </summary> - <typeparam name="T1"></typeparam> - <returns></returns> - </member> - <member name="M:IFreeSql.Select``1(System.Object)"> - <summary> - 查询数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - </summary> - <typeparam name="T1"></typeparam> - <param name="dywhere">主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合</param> - <returns></returns> - </member> - <member name="M:IFreeSql.Delete``1"> - <summary> - 删除数据 - </summary> - <typeparam name="T1"></typeparam> - <returns></returns> - </member> - <member name="M:IFreeSql.Delete``1(System.Object)"> - <summary> - 删除数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - </summary> - <typeparam name="T1"></typeparam> - <param name="dywhere">主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合</param> - <returns></returns> - </member> - <member name="M:IFreeSql.Transaction(System.Action)"> - <summary> - 开启事务(不支持异步)<para></para> - v1.5.0 关闭了线程事务超时自动提交的机制 - </summary> - <param name="handler">事务体 () => {}</param> - </member> - <member name="M:IFreeSql.Transaction(System.Data.IsolationLevel,System.Action)"> - <summary> - 开启事务(不支持异步)<para></para> - v1.5.0 关闭了线程事务超时自动提交的机制 - </summary> - <param name="isolationLevel"></param> - <param name="handler">事务体 () => {}</param> - </member> - <member name="P:IFreeSql.Ado"> - <summary> - 数据库访问对象 - </summary> - </member> - <member name="P:IFreeSql.Aop"> - <summary> - 所有拦截方法都在这里 - </summary> - </member> - <member name="P:IFreeSql.CodeFirst"> - <summary> - CodeFirst 模式开发相关方法 - </summary> - </member> - <member name="P:IFreeSql.DbFirst"> - <summary> - DbFirst 模式开发相关方法 - </summary> - </member> - <member name="P:IFreeSql.GlobalFilter"> - <summary> - 全局过滤设置,可默认附加为 Select/Update/Delete 条件 - </summary> - </member> - </members> -</doc> diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 1d65a44ca..a81d5c9f1 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -60,7 +60,8 @@ public Select0Provider() _paramsInit = _params; } - int _disposeCounter; + private int _disposeCounter; + ~Select0Provider() { if (Interlocked.Increment(ref _disposeCounter) != 1) return; @@ -83,6 +84,7 @@ public Select0Provider() } public abstract string ToSqlBase(string field = null); + public abstract void AsTableBase(Func<Type, string, string> tableRule); public static void CopyData(Select0Provider from, Select0Provider to, ReadOnlyCollection<ParameterExpression> lambParms) @@ -163,6 +165,7 @@ internal class WithTempQueryParser : BaseDiyMemberExpression { public List<InsideInfo> _insideSelectList = new List<InsideInfo>(); public List<SelectTableInfo> _outsideTable = new List<SelectTableInfo>(); + public WithTempQueryParser(Select0Provider insideSelect, SelectGroupingProvider insideSelectGroup, Expression selector, SelectTableInfo outsideTable) { if (selector != null) @@ -171,6 +174,7 @@ public WithTempQueryParser(Select0Provider insideSelect, SelectGroupingProvider _outsideTable.Add(outsideTable); } } + public class InsideInfo { public Select0Provider InsideSelect { get; } @@ -238,6 +242,7 @@ public WithTempQueryParser Append(Select0Provider select, SelectTableInfo outsid } public SelectTableInfo ParseExpMatchedTable { get; private set; } + public override string ParseExp(Expression[] members) { ParseExpMapResult = null; @@ -270,6 +275,7 @@ public override string ParseExp(Expression[] members) ParseExpMapResult = read; return $"{ParseExpMatchedTable.Alias}.{read.DbNestedField}"; } + public SelectTableInfo GetOutsideSelectTable(ParameterExpression parameterExp) { if (parameterExp == null) return null; @@ -285,6 +291,7 @@ public SelectTableInfo GetOutsideSelectTable(ParameterExpression parameterExp) public static MethodInfo _methodSqlExtInternalRawField = typeof(SqlExt).GetMethod("InternalRawField", BindingFlags.NonPublic | BindingFlags.Static); public static MethodInfo _methodSqlExtInternalRawSql = typeof(SqlExt).GetMethod("InternalRawSql", BindingFlags.NonPublic | BindingFlags.Static); + public Expression ConvertStringPropertyToExpression(string property, bool fromFirstTable = false) { if (string.IsNullOrEmpty(property)) return null; @@ -343,6 +350,7 @@ public Expression ConvertStringPropertyToExpression(string property, bool fromFi } public static MethodInfo _MethodDataReaderIsDBNull = typeof(DbDataReader).GetMethod("IsDBNull", new Type[] { typeof(int) }); + public static Dictionary<Type, MethodInfo> _dicMethodDataReaderGetValue = new Dictionary<Type, MethodInfo> { [typeof(bool)] = typeof(DbDataReader).GetMethod("GetBoolean", new Type[] { typeof(int) }), @@ -355,12 +363,14 @@ public Expression ConvertStringPropertyToExpression(string property, bool fromFi [typeof(string)] = typeof(DbDataReader).GetMethod("GetString", new Type[] { typeof(int) }), //[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) }) 有些驱动不兼容 }; + public static Dictionary<DataType, Dictionary<Type, MethodInfo>> _dicMethodDataReaderGetValueOverride = new Dictionary<DataType, Dictionary<Type, MethodInfo>>(); public static MethodInfo MethodStringContains = typeof(string).GetMethod("Contains", new[] { typeof(string) }); public static MethodInfo MethodStringStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) }); public static MethodInfo MethodStringEndsWith = typeof(string).GetMethod("EndsWith", new[] { typeof(string) }); - static ConcurrentDictionary<string, MethodInfo> MethodEnumerableDic = new ConcurrentDictionary<string, MethodInfo>(); + private static ConcurrentDictionary<string, MethodInfo> MethodEnumerableDic = new ConcurrentDictionary<string, MethodInfo>(); + public static MethodInfo GetMethodEnumerable(string methodName) => MethodEnumerableDic.GetOrAdd(methodName, et => { var methods = typeof(Enumerable).GetMethods().Where(a => a.Name == et); @@ -370,6 +380,7 @@ public static MethodInfo GetMethodEnumerable(string methodName) => MethodEnumera }); public List<NativeTuple<string, DbParameter[], ReadAnonymousTypeOtherInfo>> _SameSelectPendingShareData; + internal Select0Provider SetSameSelectPendingShareData(List<NativeTuple<string, DbParameter[], ReadAnonymousTypeOtherInfo>> data) { _SameSelectPendingShareData = data; @@ -383,6 +394,7 @@ internal Select0Provider SetSameSelectPendingShareData(List<NativeTuple<string, } return this; } + internal bool SameSelectPending(ref string sql, ReadAnonymousTypeOtherInfo csspsod) { if (_SameSelectPendingShareData != null) @@ -404,6 +416,7 @@ internal bool SameSelectPending(ref string sql, ReadAnonymousTypeOtherInfo cssps } return false; } + internal static Expression SetSameSelectPendingShareDataWithExpression(Expression exp, List<NativeTuple<string, DbParameter[], ReadAnonymousTypeOtherInfo>> data) { var callExp = exp as MethodCallExpression; @@ -449,6 +462,7 @@ public ReadAnonymousTypeAfInfo GetExpressionField(Expression newexp, FieldAliasO _commonExpression.ReadAnonymousField(_tables, _tableRule, field, map, ref index, newexp, this, _diymemexpWithTempQuery, _whereGlobalFilter, null, null, true); return new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); } + public string GetNestSelectSql(Expression select, string affield, Func<string, string> ToSql) { var allMemExps = new FindAllMemberExpressionVisitor(this); @@ -481,10 +495,12 @@ public string GetNestSelectSql(Expression select, string affield, Func<string, s field.Clear(); return sql; } + public class FindAllMemberExpressionVisitor : ExpressionVisitor { public List<NativeTuple<MemberExpression, ColumnInfo>> Result { get; set; } = new List<NativeTuple<MemberExpression, ColumnInfo>>(); - Select0Provider _select; + private Select0Provider _select; + public FindAllMemberExpressionVisitor(Select0Provider select) => _select = select; protected override Expression VisitMember(MemberExpression node) @@ -535,10 +551,12 @@ protected override Expression VisitMember(MemberExpression node) return base.VisitMember(node); } } + public class ReplaceMemberExpressionVisitor : ExpressionVisitor { - Expression _findExp; - Expression _replaceExp; + private Expression _findExp; + private Expression _replaceExp; + public Expression Replace(Expression exp, Expression find, Expression replace) // object repval) { _findExp = find; @@ -546,6 +564,7 @@ public Expression Replace(Expression exp, Expression find, Expression replace) / //_replaceExp = Expression.Constant(repval, find.Type); return this.Visit(exp); } + protected override Expression VisitMember(MemberExpression node) { if (_findExp == node) return _replaceExp; @@ -586,17 +605,20 @@ public TSelect WithTransaction(DbTransaction transaction) if (transaction != null) _connection = transaction.Connection; return this as TSelect; } + public TSelect WithConnection(DbConnection connection) { if (_transaction?.Connection != connection) _transaction = null; _connection = connection; return this as TSelect; } + public TSelect WithParameters(List<DbParameter> parameters) { if (parameters != null) _params = parameters; return this as TSelect; } + public TSelect CommandTimeout(int timeout) { _commandTimeout = timeout; @@ -611,6 +633,7 @@ public TSelect GroupBy(string sql, object parms = null) if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(_groupby, parms)); return this as TSelect; } + public TSelect Having(string sql, object parms = null) { if (string.IsNullOrEmpty(_groupby) || string.IsNullOrEmpty(sql)) return this as TSelect; @@ -625,18 +648,21 @@ public TSelect LeftJoin(Expression<Func<T1, bool>> exp) _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin); } + public TSelect InnerJoin(Expression<Func<T1, bool>> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin); } + public TSelect RightJoin(Expression<Func<T1, bool>> exp) { if (exp == null) return this as TSelect; _tables[0].Parameter = exp.Parameters[0]; return this.InternalJoin(exp?.Body, SelectTableInfoType.RightJoin); } + public TSelect LeftJoin<T2>(Expression<Func<T1, T2, bool>> exp) { if (exp == null) return this as TSelect; @@ -644,6 +670,7 @@ public TSelect LeftJoin<T2>(Expression<Func<T1, T2, bool>> exp) if (_tables.Count > 1 && _tables[1].Table.Type == typeof(T2)) _tables[1].Parameter = exp.Parameters[1]; return this.InternalJoin(exp?.Body, SelectTableInfoType.LeftJoin); } + public TSelect InnerJoin<T2>(Expression<Func<T1, T2, bool>> exp) { if (exp == null) return this as TSelect; @@ -651,6 +678,7 @@ public TSelect InnerJoin<T2>(Expression<Func<T1, T2, bool>> exp) if (_tables.Count > 1 && _tables[1].Table.Type == typeof(T2)) _tables[1].Parameter = exp.Parameters[1]; return this.InternalJoin(exp?.Body, SelectTableInfoType.InnerJoin); } + public TSelect RightJoin<T2>(Expression<Func<T1, T2, bool>> exp) { if (exp == null) return this as TSelect; @@ -666,6 +694,7 @@ public TSelect InnerJoin(string sql, object parms = null) if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } + public TSelect LeftJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; @@ -673,6 +702,7 @@ public TSelect LeftJoin(string sql, object parms = null) if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } + public TSelect RightJoin(string sql, object parms = null) { if (string.IsNullOrEmpty(sql)) return this as TSelect; @@ -680,6 +710,7 @@ public TSelect RightJoin(string sql, object parms = null) if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } + public TSelect RawJoin(string sql) { if (string.IsNullOrEmpty(sql)) return this as TSelect; @@ -700,14 +731,17 @@ public TSelect Limit(int limit) _limit = limit; return this as TSelect; } + public TSelect Master() { _select = $" {_select.Trim()} "; return this as TSelect; } + public TSelect Offset(int offset) => this.Skip(offset) as TSelect; public TSelect OrderBy(string sql, object parms = null) => this.OrderBy(true, sql, parms); + public TSelect OrderBy(bool condition, string sql, object parms = null) { if (condition == false) return this as TSelect; @@ -721,6 +755,7 @@ public TSelect OrderBy(bool condition, string sql, object parms = null) if (parms != null) _params.AddRange(_commonUtils.GetDbParamtersByObject(sql, parms)); return this as TSelect; } + public TSelect Page(int pageNumber, int pageSize) { this.Skip(Math.Max(0, pageNumber - 1) * pageSize); @@ -739,6 +774,7 @@ public TSelect Skip(int offset) _skip = offset; return this as TSelect; } + public TSelect Take(int limit) => this.Limit(limit) as TSelect; public TSelect Distinct() @@ -747,7 +783,7 @@ public TSelect Distinct() return this as TSelect; } - string GetToDeleteWhere(string alias) + private string GetToDeleteWhere(string alias) { var pks = _tables[0].Table.Primarys; var old_selectVal = _select; @@ -760,6 +796,7 @@ string GetToDeleteWhere(string alias) case DataType.Firebird: case DataType.GBase: break; + default: _select = "SELECT "; break; @@ -794,6 +831,7 @@ string GetToDeleteWhere(string alias) _select = old_selectVal; } } + public IDelete<T1> ToDelete() { if (_tables[0].Table.Primarys.Any() == false) throw new Exception(CoreErrorStrings.Entity_Must_Primary_Key("ToDelete", _tables[0].Table.CsName)); @@ -816,6 +854,7 @@ public IDelete<T1> ToDelete() case DataType.Firebird: case DataType.GBase: break; + default: var beforeSql = this._select; if (beforeSql.EndsWith("SELECT ", StringComparison.OrdinalIgnoreCase)) @@ -828,6 +867,7 @@ public IDelete<T1> ToDelete() } return del.Where(GetToDeleteWhere("ftb_del")); } + public IUpdate<T1> ToUpdate() { if (_tables[0].Table.Primarys.Any() == false) throw new Exception(CoreErrorStrings.Entity_Must_Primary_Key("ToUpdate", _tables[0].Table.CsName)); @@ -850,6 +890,7 @@ public IUpdate<T1> ToUpdate() case DataType.Firebird: case DataType.GBase: break; + default: var beforeSql = this._select; if (beforeSql.EndsWith("SELECT ", StringComparison.OrdinalIgnoreCase)) @@ -993,7 +1034,9 @@ string[] LocalGetTableNames(SelectTableInfo tb) } return unions; } + public override void AsTableBase(Func<Type, string, string> tableRule) => AsTable(tableRule); + public TSelect AsTable(Func<Type, string, string> tableRule) { if (_tableRules.Count == 1 && _diymemexpWithTempQuery != null && _diymemexpWithTempQuery is WithTempQueryParser tempQueryParser) @@ -1011,11 +1054,13 @@ public TSelect AsTable(Func<Type, string, string> tableRule) if (tableRule != null) _tableRules.Add(tableRule); return this as TSelect; } + public TSelect AsAlias(Func<Type, string, string> aliasRule) { if (aliasRule != null) _aliasRule = aliasRule; return this as TSelect; } + public TSelect AsType(Type entityType) { if (entityType == typeof(object)) throw new Exception(CoreErrorStrings.TypeAsType_NotSupport_Object("ISelect")); @@ -1025,10 +1070,13 @@ public TSelect AsType(Type entityType) if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(entityType); return this as TSelect; } + public override string ToSqlBase(string field = null) => ToSql(field); + public abstract string ToSql(string field = null); public TSelect Where(string sql, object parms = null) => this.WhereIf(true, sql, parms); + public TSelect WhereIf(bool condition, string sql, object parms = null) { if (condition == false || string.IsNullOrEmpty(sql)) return this as TSelect; @@ -1038,6 +1086,7 @@ public TSelect WhereIf(bool condition, string sql, object parms = null) } public TSelect OrderByPropertyName(string property, bool isAscending = true) => OrderByPropertyNameIf(true, property, isAscending); + public TSelect OrderByPropertyNameIf(bool condition, string property, bool isAscending = true) { if (condition == false) return this as TSelect; @@ -1053,13 +1102,30 @@ public TSelect WhereDynamicFilter(DynamicFilterInfo filter) if (filter == null) return this as TSelect; var sb = new StringBuilder(); if (IsIgnoreFilter(filter)) filter.Field = ""; + + //使用新模式的判断条件,只在第一个过滤器设置即可 + bool useAllLogic = filter.UseAllLogic; ParseFilter(DynamicFilterLogic.And, filter, true); + + //新模式下替换可能与括号连在一起的逻辑运算符 + if (useAllLogic) + { + sb = sb.Replace("( AND", "(") + .Replace("( OR", "("); + } + this.Where(sb.ToString()); sb.Clear(); return this as TSelect; void ParseFilter(DynamicFilterLogic logic, DynamicFilterInfo fi, bool isend) { + //新模式下使用过滤器自身的逻辑运算符 + if (useAllLogic) + { + logic = fi.Logic; + } + if (string.IsNullOrEmpty(fi.Field) == false) { Expression exp = null; @@ -1092,6 +1158,7 @@ void ParseFilter(DynamicFilterLogic logic, DynamicFilterInfo fi, bool isend) exp = ConvertStringPropertyToExpression(fi.Field); if (exp.Type != typeof(string)) exp = Expression.TypeAs(exp, typeof(string)); break; + default: exp = ConvertStringPropertyToExpression(fi.Field); if (exp.Type == typeof(object) && fi.Value != null) @@ -1127,10 +1194,13 @@ void ParseFilter(DynamicFilterLogic logic, DynamicFilterInfo fi, bool isend) case DynamicFilterOperator.Range: var fiValueRangeArray = getFiListValue(); if (fiValueRangeArray.Length != 2) throw new ArgumentException(CoreErrorStrings.Range_Comma_Separateda_By2Char); + exp = Expression.AndAlso( - Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[0]), exp.Type)), - Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[1]), exp.Type))); + Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[0]), exp.Type)), + Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueRangeArray[1]), exp.Type))); + break; + case DynamicFilterOperator.DateRange: var fiValueDateRangeArray = getFiListValue(); if (fiValueDateRangeArray?.Length != 2) throw new ArgumentException(CoreErrorStrings.DateRange_Comma_Separateda_By2Char); @@ -1149,7 +1219,9 @@ void ParseFilter(DynamicFilterLogic logic, DynamicFilterInfo fi, bool isend) exp = Expression.AndAlso( Expression.GreaterThanOrEqual(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueDateRangeArray[0]), exp.Type)), Expression.LessThan(exp, Expression.Constant(Utils.GetDataReaderValue(exp.Type, fiValueDateRangeArray[1]), exp.Type))); + break; + case DynamicFilterOperator.Any: case DynamicFilterOperator.NotAny: var fiValueAnyArray = getFiListValue(); @@ -1198,6 +1270,10 @@ string[] getFiListValue() } var sql = _commonExpression.ExpressionWhereLambda(_tables, _tableRule, exp, _diymemexpWithTempQuery, null, _params); + if (useAllLogic) + { + sql = " " + fi.Logic.ToString().ToUpper() + " " + sql; + } sb.Append(sql); } @@ -1207,16 +1283,33 @@ string[] getFiListValue() if (fi.Filters.Any()) { - if (string.IsNullOrEmpty(fi.Field) == false) - sb.Append(" AND "); - if (fi.Logic == DynamicFilterLogic.Or) sb.Append("("); - for (var x = 0; x < fi.Filters.Count; x++) - ParseFilter(fi.Logic, fi.Filters[x], x == fi.Filters.Count - 1); - if (fi.Logic == DynamicFilterLogic.Or) sb.Append(")"); + //新模式下,直接添加分组的逻辑运算符和括号 + if (useAllLogic) + { + //第一个条件不用添加逻辑运算符 + if (fi != filter) + { + sb.Append(" " + fi.Logic.ToString().ToUpper() + " "); + } + sb.Append("("); + for (var x = 0; x < fi.Filters.Count; x++) + ParseFilter(fi.Logic, fi.Filters[x], x == fi.Filters.Count - 1); + sb.Append(")"); + } + else + { + if (string.IsNullOrEmpty(fi.Field) == false) + sb.Append(" AND "); + if (fi.Logic == DynamicFilterLogic.Or) sb.Append("("); + for (var x = 0; x < fi.Filters.Count; x++) + ParseFilter(fi.Logic, fi.Filters[x], x == fi.Filters.Count - 1); + if (fi.Logic == DynamicFilterLogic.Or) sb.Append(")"); + } } } - if (isend == false) + //新模式下不用添加结尾的逻辑运算符 + if (isend == false && useAllLogic == false) { if (string.IsNullOrEmpty(fi.Field) == false || fi.Filters?.Any() == true) { @@ -1236,8 +1329,10 @@ bool IsIgnoreFilter(DynamicFilterInfo testFilter) string.IsNullOrEmpty(testFilter.Value?.ToString()); } } - static ConcurrentDictionary<MethodInfo, bool> _dicMethodIsDynamicFilterCustomAttribute = new ConcurrentDictionary<MethodInfo, bool>(); - static bool MethodIsDynamicFilterCustomAttribute(MethodInfo method) => _dicMethodIsDynamicFilterCustomAttribute.GetOrAdd(method, m => + + private static ConcurrentDictionary<MethodInfo, bool> _dicMethodIsDynamicFilterCustomAttribute = new ConcurrentDictionary<MethodInfo, bool>(); + + private static bool MethodIsDynamicFilterCustomAttribute(MethodInfo method) => _dicMethodIsDynamicFilterCustomAttribute.GetOrAdd(method, m => { object[] attrs = null; try @@ -1270,6 +1365,7 @@ public TSelect DisableGlobalFilter(params string[] name) } return this as TSelect; } + public TSelect ForUpdate(bool noawait = false) { if (_transaction == null && _orm.Ado.TransactionCurrentThread != null) this.WithTransaction(_orm.Ado.TransactionCurrentThread); @@ -1282,29 +1378,35 @@ public TSelect ForUpdate(bool noawait = false) case DataType.CustomMySql: _tosqlAppendContent = $"{_tosqlAppendContent} for update"; break; + case DataType.SqlServer: case DataType.OdbcSqlServer: case DataType.CustomSqlServer: _aliasRule = (_, old) => $"{old} With(UpdLock, RowLock{(noawait ? ", NoWait" : "")})"; break; + case DataType.PostgreSQL: case DataType.OdbcPostgreSQL: case DataType.CustomPostgreSQL: case DataType.KingbaseES: _tosqlAppendContent = $"{_tosqlAppendContent} for update{(noawait ? " nowait" : "")}"; break; + case DataType.Oracle: case DataType.OdbcOracle: case DataType.CustomOracle: case DataType.Dameng: _tosqlAppendContent = $"{_tosqlAppendContent} for update{(noawait ? " nowait" : "")}"; break; + case DataType.Sqlite: break; + case DataType.GBase: case DataType.ShenTong: //神通测试中发现,不支持 nowait _tosqlAppendContent = $"{_tosqlAppendContent} for update"; break; + case DataType.Firebird: _tosqlAppendContent = $"{_tosqlAppendContent} for update with lock"; break; @@ -1346,6 +1448,7 @@ public bool Any() this.Limit(1); return this.ToList<int>($"{1}{_commonUtils.FieldAsAlias("as1")}").Sum() > 0; //这里的 Sum 为了分表查询 } + public long Count() { var tmpOrderBy = _orderby; @@ -1370,12 +1473,15 @@ public long Count() _distinct = tmpDistinct; } } + public TSelect Count(out long count) { count = this.Count(); return this as TSelect; } + public List<T1> ToList() => ToList(false); + public virtual List<T1> ToList(bool includeNestedMembers) { if (_diymemexpWithTempQuery != null && _diymemexpWithTempQuery is WithTempQueryParser withTempQueryParser) @@ -1395,21 +1501,25 @@ public virtual List<T1> ToList(bool includeNestedMembers) if (_selectExpression != null) return this.InternalToList<T1>(_selectExpression); return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } + public T1 ToOne() { this.Limit(1); return this.ToList().FirstOrDefault(); } + public T1 First() => this.ToOne(); #if net40 #else - async public Task<bool> AnyAsync(CancellationToken cancellationToken = default) + + public async Task<bool> AnyAsync(CancellationToken cancellationToken = default) { this.Limit(1); return (await this.ToListAsync<int>($"1{_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Sum() > 0; //这里的 Sum 为了分表查询 } - async public Task<long> CountAsync(CancellationToken cancellationToken = default) + + public async Task<long> CountAsync(CancellationToken cancellationToken = default) { var tmpOrderBy = _orderby; var tmpSkip = _skip; @@ -1433,7 +1543,9 @@ async public Task<long> CountAsync(CancellationToken cancellationToken = default _distinct = tmpDistinct; } } + public Task<List<T1>> ToListAsync(CancellationToken cancellationToken = default) => ToListAsync(false, cancellationToken); + public virtual Task<List<T1>> ToListAsync(bool includeNestedMembers = false, CancellationToken cancellationToken = default) { if (_diymemexpWithTempQuery != null && _diymemexpWithTempQuery is WithTempQueryParser withTempQueryParser) @@ -1453,12 +1565,15 @@ public virtual Task<List<T1>> ToListAsync(bool includeNestedMembers = false, Can if (_selectExpression != null) return this.InternalToListAsync<T1>(_selectExpression, cancellationToken); return this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null, cancellationToken); } - async public Task<T1> ToOneAsync(CancellationToken cancellationToken = default) + + public async Task<T1> ToOneAsync(CancellationToken cancellationToken = default) { this.Limit(1); return (await this.ToListAsync(false, cancellationToken)).FirstOrDefault(); } + public Task<T1> FirstAsync(CancellationToken cancellationToken = default) => this.ToOneAsync(cancellationToken); + #endif } } \ No newline at end of file diff --git a/FreeSql/Internal/Model/DynamicFilterInfo.cs b/FreeSql/Internal/Model/DynamicFilterInfo.cs index 6e682207e..1cbb2a345 100644 --- a/FreeSql/Internal/Model/DynamicFilterInfo.cs +++ b/FreeSql/Internal/Model/DynamicFilterInfo.cs @@ -16,16 +16,25 @@ namespace FreeSql.Internal.Model [Serializable] public class DynamicFilterInfo { + /// <summary> + /// 使用所有逻辑运算符,默认为False。 + /// False:按照原来的模式,只有分组的逻辑运算符会生成到查询条件中,只有OR分组会生成括号。 + /// True:按照新的模式,所有的逻辑运算符都会生成到查询条件中,所有分组(AND或OR)都会生成括号。 + /// </summary> + public bool UseAllLogic { get; set; } + /// <summary> /// 属性名:Name<para></para> /// 导航属性:Parent.Name<para></para> /// 多表:b.Name<para></para> /// </summary> public string Field { get; set; } + /// <summary> /// 操作符 /// </summary> public DynamicFilterOperator Operator { get; set; } + /// <summary> /// 值 /// </summary> @@ -35,6 +44,7 @@ public class DynamicFilterInfo /// Filters 下的逻辑运算符 /// </summary> public DynamicFilterLogic Logic { get; set; } + /// <summary> /// 子过滤条件,它与当前的逻辑关系是 And<para></para> /// 注意:当前 Field 可以留空 @@ -42,13 +52,16 @@ public class DynamicFilterInfo public List<DynamicFilterInfo> Filters { get; set; } } - public enum DynamicFilterLogic { And, Or } + public enum DynamicFilterLogic + { And, Or } + public enum DynamicFilterOperator { /// <summary> /// like /// </summary> Contains, + StartsWith, EndsWith, NotContains, @@ -60,16 +73,19 @@ public enum DynamicFilterOperator /// Equal/Equals/Eq 效果相同 /// </summary> Equal, + /// <summary> /// =<para></para> /// Equal/Equals/Eq 效果相同 /// </summary> Equals, + /// <summary> /// =<para></para> /// Equal/Equals/Eq 效果相同 /// </summary> Eq, + /// <summary> /// <> /// </summary> @@ -79,14 +95,17 @@ public enum DynamicFilterOperator /// > /// </summary> GreaterThan, + /// <summary> /// >= /// </summary> GreaterThanOrEqual, + /// <summary> /// < /// </summary> LessThan, + /// <summary> /// <= /// </summary> @@ -116,6 +135,7 @@ public enum DynamicFilterOperator /// 此时 Value 的值格式为逗号分割:value1,value2,value3... 或者数组 /// </summary> Any, + /// <summary> /// not in (1,2,3)<para></para> /// 此时 Value 的值格式为逗号分割:value1,value2,value3... 或者数组 @@ -143,5 +163,6 @@ public enum DynamicFilterOperator /// 授权 DynamicFilter 支持 Custom 自定义解析 /// </summary> [AttributeUsage(AttributeTargets.Method)] - public class DynamicFilterCustomAttribute : Attribute { } -} + public class DynamicFilterCustomAttribute : Attribute + { } +} \ No newline at end of file