using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Text.RegularExpressions; namespace SqlSugar { public abstract class QueryBuilder : IDMLBuilder { protected QueryBuilder() { this.Parameters = new List(); } #region Private Fileds protected List _JoinQueryInfos; protected Dictionary _EasyJoinInfos; private List _WhereInfos; private string _HavingInfos; protected string _TableNameString; #endregion Private Fileds #region Service object public StringBuilder sql { get; set; } public SqlSugarClient Context { get; set; } public ILambdaExpressions LambdaExpressions { get; set; } public ISqlBuilder Builder { get; set; } #endregion Service object #region Splicing basic public bool IsCount { get; set; } public int? Skip { get; set; } public int ExternalPageIndex { get; set; } public int ExternalPageSize { get; set; } public int? Take { get; set; } public string OrderByValue { get; set; } public object SelectValue { get; set; } public string SelectCacheKey { get; set; } public string EntityName { get; set; } public Type EntityType { get; set; } public string TableWithString { get; set; } public string GroupByValue { get; set; } public string PartitionByValue { get; set; } public int WhereIndex { get; set; } public int JoinIndex { get; set; } public bool IsDisabledGobalFilter { get; set; } public virtual List Parameters { get; set; } public Dictionary EasyJoinInfos { get { _EasyJoinInfos = UtilMethods.IsNullReturnNew(_EasyJoinInfos); return _EasyJoinInfos; } set { _EasyJoinInfos = value; } } public virtual List JoinQueryInfos { get { _JoinQueryInfos = UtilMethods.IsNullReturnNew(_JoinQueryInfos); return _JoinQueryInfos; } set { _JoinQueryInfos = value; } } public virtual string TableShortName { get; set; } public virtual List WhereInfos { get { _WhereInfos = UtilMethods.IsNullReturnNew(_WhereInfos); return _WhereInfos; } set { _WhereInfos = value; } } public virtual string HavingInfos { get { return _HavingInfos; } set { _HavingInfos = value; } } #endregion Splicing basic #region Lambada Type public ResolveExpressType SelectType { get { return this.IsSingle() ? ResolveExpressType.SelectSingle : ResolveExpressType.SelectMultiple; } } public ResolveExpressType WheretType { get { return this.IsSingle() ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple; } } #endregion Lambada Type #region Sql Template public virtual string SqlTemplate { get { return "SELECT {0} FROM {1}{2}{3}{4} "; } } public virtual string JoinTemplate { get { return "{0}JOIN {1}{2}ON {3} "; } } public virtual string PageTempalte { get { return @"SELECT * FROM ({0}) T WHERE RowIndex BETWEEN {1} AND {2}"; } } public virtual string ExternalPageTempalte { get { return @"SELECT * FROM ({0}) T WHERE RowIndex2 BETWEEN {1} AND {2}"; } } public virtual string DefaultOrderByTemplate { get { return "ORDER BY " + this.Builder.SqlDateNow + " "; } } public virtual string OrderByTemplate { get { return "ORDER BY "; } } public virtual string GroupByTemplate { get { return "GROUP BY "; } } public virtual string PartitionByTemplate { get { return "PARTITION BY "; } } public virtual string MaxTemplate { get { return "MAX({0})"; } } public virtual string MinTemplate { get { return "MIN({0})"; } } public virtual string SumTemplate { get { return "SUM({0})"; } } public virtual string AvgTemplate { get { return "AVG({0})"; } } public virtual string InTemplate { get { return "{0} IN ({1}) "; } } #endregion Sql Template #region Common Methods public virtual bool IsSingle() { var isSingle = Builder.QueryBuilder.JoinQueryInfos.IsNullOrEmpty() && !EasyJoinInfos.Any(); return isSingle; } public virtual ExpressionResult GetExpressionValue(Expression expression, ResolveExpressType resolveType) { var resolveExpress = this.LambdaExpressions; if (resolveType.IsIn(ResolveExpressType.FieldSingle, ResolveExpressType.FieldMultiple, ResolveExpressType.SelectSingle, ResolveExpressType.SelectMultiple) && (expression is LambdaExpression) && (expression as LambdaExpression).Body is BinaryExpression) { resolveType = resolveType.IsIn(ResolveExpressType.SelectSingle, ResolveExpressType.FieldSingle) ? ResolveExpressType.WhereSingle : ResolveExpressType.WhereMultiple; } this.LambdaExpressions.Clear(); resolveExpress.JoinQueryInfos = Builder.QueryBuilder.JoinQueryInfos; resolveExpress.IsSingle = IsSingle(); resolveExpress.MappingColumns = Context.MappingColumns; resolveExpress.MappingTables = Context.MappingTables; resolveExpress.IgnoreComumnList = Context.IgnoreColumns; resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices?.SqlFuncServices; resolveExpress.InitMappingInfo = this.Context.InitMppingInfo; resolveExpress.RefreshMapping = () => { resolveExpress.MappingColumns = Context.MappingColumns; resolveExpress.MappingTables = Context.MappingTables; resolveExpress.IgnoreComumnList = Context.IgnoreColumns; resolveExpress.SqlFuncServices = Context.CurrentConnectionConfig.ConfigureExternalServices?.SqlFuncServices; }; resolveExpress.Resolve(expression, resolveType); this.Parameters.AddRange(resolveExpress.Parameters); var result = resolveExpress.Result; var isSingleTableHasSubquery = IsSingle() && resolveExpress.SingleTableNameSubqueryShortName.HasValue(); if (isSingleTableHasSubquery) { Check.Exception(!string.IsNullOrEmpty(this.TableShortName) && resolveExpress.SingleTableNameSubqueryShortName != this.TableShortName, "{0} and {1} need same name"); this.TableShortName = resolveExpress.SingleTableNameSubqueryShortName; } return result; } public virtual string ToSqlString() { var oldOrderBy = this.OrderByValue; var externalOrderBy = oldOrderBy; var isIgnoreOrderBy = this.IsCount && this.PartitionByValue.IsNullOrEmpty(); AppendFilter(); sql = new StringBuilder(); if (this.OrderByValue == null && (Skip != null || Take != null)) this.OrderByValue = " ORDER BY GetDate() "; if (this.PartitionByValue.HasValue()) { this.OrderByValue = this.PartitionByValue + this.OrderByValue; } var isRowNumber = Skip != null || Take != null; var rowNumberString = string.Format(",ROW_NUMBER() OVER({0}) AS RowIndex ", GetOrderByString); var groupByValue = GetGroupByString + HavingInfos; var orderByValue = (!isRowNumber && this.OrderByValue.HasValue()) ? GetOrderByString : null; if (isIgnoreOrderBy) { orderByValue = null; } sql.AppendFormat(SqlTemplate, GetSelectValue, GetTableNameString, GetWhereValueString, groupByValue, orderByValue); sql.Replace(UtilConstants.ReplaceKey, isRowNumber ? (isIgnoreOrderBy ? null : rowNumberString) : null); if (isIgnoreOrderBy) { this.OrderByValue = oldOrderBy; return sql.ToString(); } var result = ToPageSql(sql.ToString(), this.Take, this.Skip); if (ExternalPageIndex > 0) { if (externalOrderBy.IsNullOrEmpty()) { externalOrderBy = " ORDER BY GetDate() "; } result = string.Format("SELECT *,ROW_NUMBER() OVER({0}) AS RowIndex2 FROM ({1}) ExternalTable ", GetExternalOrderBy(externalOrderBy), result); result = ToPageSql2(result, ExternalPageIndex, ExternalPageSize, true); } this.OrderByValue = oldOrderBy; return result; } public virtual void AppendFilter() { if (!IsDisabledGobalFilter && this.Context.QueryFilter.GeFilterList.HasValue()) { var gobalFilterList = this.Context.QueryFilter.GeFilterList.Where(it => it.FilterName.IsNullOrEmpty()).ToList(); foreach (var item in gobalFilterList.Where(it => it.IsJoinQuery == !IsSingle())) { var filterResult = item.FilterValue(this.Context); WhereInfos.Add(this.Builder.AppendWhereOrAnd(this.WhereInfos.IsNullOrEmpty(), filterResult.Sql)); var filterParamters = this.Context.Ado.GetParameters(filterResult.Parameters); if (filterParamters.HasValue()) { this.Parameters.AddRange(filterParamters); } } } } public virtual string GetExternalOrderBy(string externalOrderBy) { return Regex.Replace(externalOrderBy, @"\[\w+\]\.", ""); } public virtual string ToCountSql(string sql) { return string.Format(" SELECT COUNT(1) FROM ({0}) CountTable ", sql); } public virtual string ToPageSql(string sql, int? take, int? skip, bool isExternal = false) { var temp = isExternal ? ExternalPageTempalte : PageTempalte; if (skip != null && take == null) { return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, long.MaxValue); } else if (skip == null && take != null) { return string.Format(temp, sql.ToString(), 1, take.ObjToInt()); } else if (skip != null && take != null) { return string.Format(temp, sql.ToString(), skip.ObjToInt() + 1, skip.ObjToInt() + take.ObjToInt()); } else { return sql.ToString(); } } public virtual string ToPageSql2(string sql, int? pageIndex, int? pageSize, bool isExternal = false) { var temp = isExternal ? ExternalPageTempalte : PageTempalte; return string.Format(temp, sql.ToString(), (pageIndex - 1) * pageSize + 1, pageIndex * pageSize); } public virtual string ToJoinString(JoinQueryInfo joinInfo) { return string.Format( this.JoinTemplate, joinInfo.JoinType.ToString() + UtilConstants.Space, Builder.GetTranslationTableName(joinInfo.TableName) + UtilConstants.Space, joinInfo.ShortName + UtilConstants.Space + TableWithString, joinInfo.JoinWhere); } public virtual void Clear() { this.Skip = 0; this.Take = 0; this.sql = null; this.WhereIndex = 0; this.Parameters = null; this.GroupByValue = null; this._TableNameString = null; this.WhereInfos = null; this.JoinQueryInfos = null; } public virtual bool IsComplexModel(string sql) { return Regex.IsMatch(sql, @"AS \[\w+\.\w+\]"); } #endregion Common Methods #region Get SQL Partial public virtual string GetSelectValue { get { var result = string.Empty; result = this.SelectValue == null || this.SelectValue is string ? GetSelectValueByString() : GetSelectValueByExpression(); if (this.SelectType == ResolveExpressType.SelectMultiple) { this.SelectCacheKey = this.SelectCacheKey + string.Join("-", this._JoinQueryInfos.Select(it => it.TableName)); } return result; } } public virtual string GetSelectValueByExpression() { var expression = this.SelectValue as Expression; var result = GetExpressionValue(expression, this.SelectType).GetResultString(); this.SelectCacheKey = result; return result; } public virtual string GetSelectValueByString() { string result; if (this.SelectValue.IsNullOrEmpty()) { string pre = null; if (this.JoinQueryInfos.HasValue() && this.JoinQueryInfos.Any(it => TableShortName.HasValue())) { pre = Builder.GetTranslationColumnName(TableShortName) + "."; } result = string.Join(",", this.Context.EntityMaintenance.GetEntityInfo(this.EntityType).Columns.Where(it => !it.IsIgnore).Select(it => pre + Builder.GetTranslationColumnName(it.EntityName, it.PropertyName))); } else { result = this.SelectValue.ObjToString(); this.SelectCacheKey = result; } if (result.IsNullOrEmpty()) { result = "*"; } return result; } public virtual string GetWhereValueString { get { if (this.WhereInfos == null) return null; else { return string.Join(UtilConstants.Space, this.WhereInfos); } } } public virtual string GetJoinValueString { get { if (this.JoinQueryInfos.IsNullOrEmpty()) return null; else { return string.Join(UtilConstants.Space, this.JoinQueryInfos.Select(it => this.ToJoinString(it))); } } } public virtual string GetTableNameString { get { var result = Builder.GetTranslationTableName(EntityName); result += UtilConstants.Space; if (this.TableShortName.HasValue()) { result += (TableShortName + UtilConstants.Space); } if (this.TableWithString.HasValue() && this.TableWithString != SqlWith.Null) { result += TableWithString + UtilConstants.Space; } if (!this.IsSingle()) { result += GetJoinValueString + UtilConstants.Space; } if (this.EasyJoinInfos.IsValuable()) { if (this.TableWithString.HasValue() && this.TableWithString != SqlWith.Null) { result += "," + string.Join(",", this.EasyJoinInfos.Select(it => string.Format("{0} {1} {2} ", GetTableName(it.Value), it.Key, TableWithString))); } else { result += "," + string.Join(",", this.EasyJoinInfos.Select(it => string.Format("{0} {1} ", GetTableName(it.Value), it.Key))); } } return result; } } public virtual string GetOrderByString { get { if (this.OrderByValue == null) return null; if (IsCount && this.PartitionByValue.IsNullOrEmpty()) return null; else { return this.OrderByValue; } } } public virtual string GetGroupByString { get { if (this.GroupByValue == null) return null; if (this.GroupByValue.Last() != ' ') { return this.GroupByValue + UtilConstants.Space; } return this.GroupByValue; } } #endregion Get SQL Partial private string GetTableName(string entityName) { var result = this.Context.EntityMaintenance.GetTableName(entityName); return this.Builder.GetTranslationTableName(result); } } }