using FreeSql.DataAnnotations; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading; using static FreeSql.SqlExtExtensions; [ExpressionCall] public static class FreeSqlGlobalExpressionCallExtensions { public static ThreadLocal expContext = new ThreadLocal(); /// /// C#: that >= between && that <= and /// SQL: that BETWEEN between AND and /// /// /// /// /// public static bool Between(this DateTime that, DateTime between, DateTime and) { if (expContext.IsValueCreated == false || expContext.Value == null || expContext.Value.ParsedContent == null) return that >= between && that <= and; expContext.Value.Result = $"{expContext.Value.ParsedContent["that"]} between {expContext.Value.ParsedContent["between"]} and {expContext.Value.ParsedContent["and"]}"; return false; } /// /// 注意:这个方法和 Between 有细微区别 /// C#: that >= start && that < end /// SQL: that >= start and that < end /// /// /// /// /// public static bool BetweenEnd(this DateTime that, DateTime start, DateTime end) { if (expContext.IsValueCreated == false || expContext.Value == null || expContext.Value.ParsedContent == null) return that >= start && that < end; expContext.Value.Result = $"{expContext.Value.ParsedContent["that"]} >= {expContext.Value.ParsedContent["start"]} and {expContext.Value.ParsedContent["that"]} < {expContext.Value.ParsedContent["end"]}"; return false; } } namespace FreeSql { /// /// SqlExt 是利用自定表达式函数解析功能,解析默认常用的SQL函数,欢迎 PR /// [ExpressionCall] public static class SqlExt { internal static ThreadLocal expContext = new ThreadLocal(); #region SqlServer/PostgreSQL over /// /// rank() over(order by ...) /// /// public static ISqlOver Rank() => Over("rank()"); /// /// dense_rank() over(order by ...) /// /// public static ISqlOver DenseRank() => Over("dense_rank()"); /// /// count() over(order by ...) /// /// public static ISqlOver Count() => Over("count()"); /// /// sum(..) over(order by ...) /// /// /// public static ISqlOver Sum(object column) => Over($"sum({expContext.Value.ParsedContent["column"]})"); /// /// avg(..) over(order by ...) /// /// public static ISqlOver Avg() => Over($"avg({expContext.Value.ParsedContent["column"]})"); /// /// max(..) over(order by ...) /// /// /// /// public static ISqlOver Max(T column) => Over($"max({expContext.Value.ParsedContent["column"]})"); /// /// min(..) over(order by ...) /// /// /// /// public static ISqlOver Min(T column) => Over($"min({expContext.Value.ParsedContent["column"]})"); /// /// SqlServer row_number() over(order by ...) /// /// public static ISqlOver RowNumber() => Over("row_number()"); #endregion /// /// isnull、ifnull、coalesce、nvl /// /// /// /// /// public static TValue IsNull(TValue value, TValue defaultValue) { expContext.Value.Result = expContext.Value._commonExp._common.IsNull(expContext.Value.ParsedContent["value"], expContext.Value.ParsedContent["defaultValue"]); return default(TValue); } /// /// count(distinct name) /// /// /// /// public static long DistinctCount(T column) { expContext.Value.Result = $"count(distinct {expContext.Value.ParsedContent["column"]})"; return 0; } #region 大小判断 /// /// 大于 > /// /// public static bool GreaterThan(TValue value1, TValue value2) { expContext.Value.Result = $"{expContext.Value.ParsedContent["value1"]} > {expContext.Value.ParsedContent["value2"]}"; return false; } /// /// 大于或等于 >= /// /// public static bool GreaterThanOrEqual(TValue value1, TValue value2) { expContext.Value.Result = $"{expContext.Value.ParsedContent["value1"]} >= {expContext.Value.ParsedContent["value2"]}"; return false; } /// /// 小于 < /// /// public static bool LessThan(TValue value1, TValue value2) { expContext.Value.Result = $"{expContext.Value.ParsedContent["value1"]} < {expContext.Value.ParsedContent["value2"]}"; return false; } /// /// 小于或等于 <= /// /// public static bool LessThanOrEqual(TValue value1, TValue value2) { expContext.Value.Result = $"{expContext.Value.ParsedContent["value1"]} <= {expContext.Value.ParsedContent["value2"]}"; return false; } #endregion /// /// case when .. then .. end /// /// public static ICaseWhenEnd Case() => SqlExtExtensions.Case(); /// /// case when .. then .. end /// /// /// /// /// /// public static TOutput CaseDict(TInput input, [RawValue] Dictionary dict) { var ec = expContext.Value; var sb = new StringBuilder(); sb.Append("case"); foreach (var kv in dict) sb.Append(" when ").Append(ec.ParsedContent["input"]).Append(" = ").Append(ec.FormatSql(kv.Key)) .Append(" then ").Append(ec.FormatSql(kv.Value)); sb.Append(" end"); ec.Result = sb.ToString(); return default; } /// /// MySql group_concat(distinct .. order by .. separator ..) /// /// /// public static IGroupConcat GroupConcat(object column) => SqlExtExtensions.GroupConcat(column); /// /// MySql find_in_set(str, strlist) /// /// /// /// /// public static int FindInSet(TValue str, string strlist) { expContext.Value.Result = $"find_in_set({expContext.Value.ParsedContent["str"]}, {expContext.Value.ParsedContent["strlist"]})"; return 0; } /// /// PostgreSQL string_agg(.., ..) /// /// /// /// public static string StringAgg(object column, object delimiter) { expContext.Value.Result = $"string_agg({expContext.Value.ParsedContent["column"]}, {expContext.Value.ParsedContent["delimiter"]})"; return ""; } } [ExpressionCall] public static class SqlExtExtensions //这个类存在的意义,是不想使用者方法名污染 { static ThreadLocal expContextSelf = new ThreadLocal(); static ExpressionCallContext expContext => expContextSelf.Value ?? SqlExt.expContext.Value; internal static ThreadLocal> expSb = new ThreadLocal>(); internal static ExpSbInfo expSbLast => expSb.Value.Last(); internal class ExpSbInfo { public StringBuilder Sb { get; } = new StringBuilder(); public bool IsOver = false; public bool IsOrderBy = false; public bool IsDistinct = false; } #region .. over([partition by ..] order by ...) internal static ISqlOver Over(string sqlFunc) { if (expSb.Value == null) expSb.Value = new List(); expSb.Value.Add(new ExpSbInfo()); expSbLast.Sb.Append(sqlFunc); return null; } public static ISqlOver Over(this ISqlOver that) { expSbLast.Sb.Append(" over("); expSbLast.IsOver = true; return that; } public static ISqlOver PartitionBy(this ISqlOver that, object column) { var sb = expSbLast.Sb; sb.Append(" partition by "); var exp = expContext.RawExpression["column"]; if (exp.NodeType == ExpressionType.New) { var expNew = exp as NewExpression; for (var a = 0; a < expNew.Arguments.Count; a++) { if (a > 0) sb.Append(","); sb.Append(expContext.Utility.ParseExpression(expNew.Arguments[a])); } } else sb.Append(expContext.ParsedContent["column"]); return that; } public static ISqlOver OrderBy(this ISqlOver that, object column) => OrderByPriv(that, false); public static ISqlOver OrderByDescending(this ISqlOver that, object column) => OrderByPriv(that, true); static ISqlOver OrderByPriv(this ISqlOver that, bool isDesc) { var sb = expSbLast.Sb; if (expSbLast.IsOrderBy == false) { sb.Append(" order by "); expSbLast.IsOrderBy = true; } var exp = expContext.RawExpression["column"]; if (exp.NodeType == ExpressionType.New) { var expNew = exp as NewExpression; for (var a = 0; a < expNew.Arguments.Count; a++) { sb.Append(expContext.Utility.ParseExpression(expNew.Arguments[a])); if (isDesc) sb.Append(" desc"); sb.Append(","); } } else { sb.Append(expContext.ParsedContent["column"]); if (isDesc) sb.Append(" desc"); sb.Append(","); } return that; } public static TValue ToValue(this ISqlOver that) { var sql = expSbLast.Sb.ToString().TrimEnd(','); if (expSbLast.IsOver) sql = $"{sql})"; expSbLast.Sb.Clear(); expSb.Value.RemoveAt(expSb.Value.Count - 1); expContext.Result = sql; return default; } public interface ISqlOver { } #endregion #region case when .. then .. when .. then .. end public static ICaseWhenEnd Case() { if (expSb.Value == null) expSb.Value = new List(); expSb.Value.Add(new ExpSbInfo()); expSbLast.Sb.Append("case "); return null; } public static ICaseWhenEnd When(this ICaseWhenEnd that, bool test, TValue then) { expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}when ").Append(expContext.ParsedContent["test"]).Append(" then ").Append(expContext.ParsedContent["then"]); return null; } public static ICaseWhenEnd When(this ICaseWhenEnd that, bool test, TValue then) { expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}when ").Append(expContext.ParsedContent["test"]).Append(" then ").Append(expContext.ParsedContent["then"]); return null; } public static ICaseWhenEnd Else(this ICaseWhenEnd that, TValue then) { expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2)}else ").Append(expContext.ParsedContent["then"]); return null; } public static TValue End(this ICaseWhenEnd that) { var sql = expSbLast.Sb.Append($"\r\n{"".PadRight(expSb.Value.Count * 2 - 2)}end").ToString(); expSbLast.Sb.Clear(); expSb.Value.RemoveAt(expSb.Value.Count - 1); expContext.Result = sql; return default; } public interface ICaseWhenEnd { } public interface ICaseWhenEnd { } #endregion #region group_concat public static IGroupConcat GroupConcat(object column) { if (expSb.Value == null) expSb.Value = new List(); expSb.Value.Add(new ExpSbInfo()); expSbLast.Sb.Append("group_concat(").Append(expContext.ParsedContent["column"]); return null; } public static IGroupConcat Distinct(this IGroupConcat that) { if (expSbLast.IsDistinct == false) { expSbLast.Sb.Insert(expSbLast.Sb.ToString().LastIndexOf("group_concat(") + 13, "distinct "); expSbLast.IsDistinct = true; } return that; } public static IGroupConcat Separator(this IGroupConcat that, object separator) { if (expSbLast.IsOrderBy) expSbLast.Sb.Remove(expSbLast.Sb.Length - 1, 1); expSbLast.Sb.Append(" separator ").Append(expContext.ParsedContent["separator"]); return that; } public static IGroupConcat OrderBy(this IGroupConcat that, object column) => OrderByPriv(that, false); public static IGroupConcat OrderByDescending(this IGroupConcat that, object column) => OrderByPriv(that, true); static IGroupConcat OrderByPriv(this IGroupConcat that, bool isDesc) { var sb = expSbLast.Sb; if (expSbLast.IsOrderBy == false) { sb.Append(" order by "); expSbLast.IsOrderBy = true; } var exp = expContext.RawExpression["column"]; if (exp.NodeType == ExpressionType.New) { var expNew = exp as NewExpression; for (var a = 0; a < expNew.Arguments.Count; a++) { sb.Append(expContext.Utility.ParseExpression(expNew.Arguments[a])); if (isDesc) sb.Append(" desc"); sb.Append(","); } } else { sb.Append(expContext.ParsedContent["column"]); if (isDesc) sb.Append(" desc"); sb.Append(","); } return that; } public static string ToValue(this IGroupConcat that) { var sql = expSbLast.Sb.ToString().TrimEnd(','); expSbLast.Sb.Clear(); expSb.Value.RemoveAt(expSb.Value.Count - 1); expContext.Result = $"{sql})"; return default; } public interface IGroupConcat { } #endregion #region string.Join 反射处理,此块代码用于反射,所以别修改定义 public static string StringJoinSqliteGroupConcat(object column, object delimiter) { expContext.Result = $"group_concat({expContext.ParsedContent["column"]},{expContext.ParsedContent["delimiter"]})"; return null; } public static string StringJoinPgsqlGroupConcat(object column, object delimiter) { expContext.Result = $"string_agg(({expContext.ParsedContent["column"]})::text,{expContext.ParsedContent["delimiter"]})"; return null; } public static string StringJoinMySqlGroupConcat(object column, object delimiter) { expContext.Result = $"group_concat({expContext.ParsedContent["column"]} separator {expContext.ParsedContent["delimiter"]})"; return null; } public static string StringJoinOracleGroupConcat(object column, object delimiter) { string orderby = null; var subSelect = expContext._tsc?.subSelect001; if (subSelect != null) { orderby = subSelect?._orderby?.Trim('\r', '\n'); if (string.IsNullOrEmpty(orderby)) { var subSelectTb1 = subSelect._tables.FirstOrDefault(); if (subSelectTb1 != null && subSelectTb1.Table.Primarys.Any() == true) orderby = $"order by {string.Join(",", subSelectTb1.Table.Primarys.Select(a => $"{subSelectTb1.Alias}.{subSelect._commonUtils.QuoteSqlName(a.Attribute.Name)}"))}"; } } if (string.IsNullOrEmpty(orderby)) orderby = "order by 1"; expContext.Result = $"listagg(to_char({expContext.ParsedContent["column"]}),{expContext.ParsedContent["delimiter"]}) within group({orderby})"; return null; } public static string StringJoinFirebirdList(object column, object delimiter) { expContext.Result = $"list({expContext.ParsedContent["column"]},{expContext.ParsedContent["delimiter"]})"; return null; } #endregion } }