mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 lambda 表达式树解析子查询 ToList + string.Join() 产生 类似 group_concat 的效果(适配了 sqlserver/pgsql/oracle/mysql/sqlite/达梦/金仓) #405;
This commit is contained in:
		@@ -269,5 +269,41 @@ namespace FreeSql
 | 
			
		||||
        }
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -234,23 +234,28 @@ namespace System.Linq.Expressions
 | 
			
		||||
            return test.Result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static bool IsStringJoin(this MethodCallExpression exp, out MethodCallExpression joinExpArgs1Out, out LambdaExpression joinExpArgs1Args0Out)
 | 
			
		||||
        static ConcurrentDictionary<Type, ConcurrentDictionary<string, MethodInfo>> _dicTypeMethod = new ConcurrentDictionary<Type, ConcurrentDictionary<string, MethodInfo>>();
 | 
			
		||||
        public static bool IsStringJoin(this MethodCallExpression exp, out Expression tolistObjectExpOut, out MethodInfo toListMethodOut, out LambdaExpression toListArgs0Out)
 | 
			
		||||
        {
 | 
			
		||||
            if (exp.Arguments.Count == 2 &&
 | 
			
		||||
                exp.Arguments[1].NodeType == ExpressionType.Call &&
 | 
			
		||||
                exp.Arguments[1].Type.FullName.StartsWith("System.Collections.Generic.List`1") &&
 | 
			
		||||
                exp.Arguments[1] is MethodCallExpression joinExpArgs1 &&
 | 
			
		||||
                joinExpArgs1.Method.Name == "ToList" &&
 | 
			
		||||
                joinExpArgs1.Arguments.Count == 1 &&
 | 
			
		||||
                joinExpArgs1.Arguments[0] is UnaryExpression joinExpArgs1Args0Tmp &&
 | 
			
		||||
                joinExpArgs1Args0Tmp.Operand is LambdaExpression joinExpArgs1Args0)
 | 
			
		||||
                exp.Arguments[1] is MethodCallExpression toListMethod &&
 | 
			
		||||
                toListMethod.Method.Name == "ToList" &&
 | 
			
		||||
                toListMethod.Arguments.Count == 1 &&
 | 
			
		||||
                toListMethod.Arguments[0] is UnaryExpression joinExpArgs1Args0Tmp &&
 | 
			
		||||
                joinExpArgs1Args0Tmp.Operand is LambdaExpression toListArgs0)
 | 
			
		||||
            {
 | 
			
		||||
                joinExpArgs1Out = joinExpArgs1;
 | 
			
		||||
                joinExpArgs1Args0Out = joinExpArgs1Args0;
 | 
			
		||||
                tolistObjectExpOut = toListMethod.Object;
 | 
			
		||||
                toListMethodOut = toListMethod.Type.GetGenericArguments().FirstOrDefault() == typeof(string) ?
 | 
			
		||||
                    toListMethod.Method :
 | 
			
		||||
                    toListMethod.Method.GetGenericMethodDefinition().MakeGenericMethod(typeof(string));
 | 
			
		||||
                toListArgs0Out = toListArgs0;
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            joinExpArgs1Out = null;
 | 
			
		||||
            joinExpArgs1Args0Out = null;
 | 
			
		||||
            tolistObjectExpOut = null;
 | 
			
		||||
            toListMethodOut = null;
 | 
			
		||||
            toListArgs0Out = null;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -794,6 +794,14 @@
 | 
			
		||||
            <param name="column"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.SqlExt.StringAgg(System.Object,System.Object)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            PostgreSQL string_agg(.., ..)
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="column"></param>
 | 
			
		||||
            <param name="delimiter"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.FreeSqlBuilder.UseConnectionString(FreeSql.DataType,System.String,System.Type)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            使用连接串(推荐)
 | 
			
		||||
 
 | 
			
		||||
@@ -1060,6 +1060,7 @@ namespace FreeSql.Internal
 | 
			
		||||
                                        case "Max":
 | 
			
		||||
                                        case "Avg":
 | 
			
		||||
                                            var tscClone1 = tsc.CloneDisableDiyParse();
 | 
			
		||||
                                            tscClone1.subSelect001 = fsql as Select0Provider; //#405 Oracle within group(order by ..)
 | 
			
		||||
                                            tscClone1.isDisableDiyParse = false;
 | 
			
		||||
                                            tscClone1._tables = fsqltables;
 | 
			
		||||
                                            var sqlSum = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { $"{exp3.Method.Name.ToLower()}({ExpressionLambdaToSql(exp3.Arguments.FirstOrDefault(), tscClone1)})" })?.ToString();
 | 
			
		||||
@@ -1070,6 +1071,7 @@ namespace FreeSql.Internal
 | 
			
		||||
                                        case "ToOne":
 | 
			
		||||
                                        case "First":
 | 
			
		||||
                                            var tscClone2 = tsc.CloneDisableDiyParse();
 | 
			
		||||
                                            tscClone2.subSelect001 = fsql as Select0Provider; //#405 Oracle within group(order by ..)
 | 
			
		||||
                                            tscClone2.isDisableDiyParse = false;
 | 
			
		||||
                                            tscClone2._tables = fsqltables;
 | 
			
		||||
                                            var sqlFirst = fsqlType.GetMethod("ToSql", new Type[] { typeof(string) })?.Invoke(fsql, new object[] { ExpressionLambdaToSql(exp3.Arguments.FirstOrDefault(), tscClone2) })?.ToString();
 | 
			
		||||
@@ -1443,6 +1445,7 @@ namespace FreeSql.Internal
 | 
			
		||||
            public List<SelectTableInfo> _tables { get; set; }
 | 
			
		||||
            public List<SelectColumnInfo> _selectColumnMap { get; set; }
 | 
			
		||||
            public SelectGroupingProvider grouping { get; set; }
 | 
			
		||||
            public Select0Provider subSelect001 { get; set; } //#405 Oracle within group(order by ..)
 | 
			
		||||
            public SelectTableInfoType tbtype { get; set; }
 | 
			
		||||
            public bool isQuoteName { get; set; }
 | 
			
		||||
            public bool isDisableDiyParse { get; set; }
 | 
			
		||||
@@ -1509,6 +1512,7 @@ namespace FreeSql.Internal
 | 
			
		||||
                    _tables = this._tables,
 | 
			
		||||
                    _selectColumnMap = this._selectColumnMap,
 | 
			
		||||
                    grouping = this.grouping,
 | 
			
		||||
                    subSelect001 = this.subSelect001,
 | 
			
		||||
                    tbtype = this.tbtype,
 | 
			
		||||
                    isQuoteName = this.isQuoteName,
 | 
			
		||||
                    isDisableDiyParse = true,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user