diff --git a/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs b/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs index c261353e..b094b06c 100644 --- a/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/SqlServer/SqlServerExpression/StringTest.cs @@ -66,6 +66,24 @@ namespace FreeSql.Tests.SqlServerExpression list.Add(select.Where(a => a.TitleVarchar == "aaa").ToList()); } + [Fact] + public void StringJoin() + { + var fsql = g.sqlserver; + fsql.Delete().Where("1=1").ExecuteAffrows(); + fsql.Insert(new[] { new StringJoin01 { name = "北京" }, new StringJoin01 { name = "上海" }, new StringJoin01 { name = "深圳" }, }).ExecuteAffrows(); + + var val1 = string.Join(",", fsql.Select().ToList(a => a.name)) + ","; + var val2 = fsql.Select().ToList(a => string.Join(",", fsql.Select().As("b").ToList(b => b.name))); + Assert.Equal(val1, val2[0]); + } + class StringJoin01 + { + [Column(IsIdentity = true)] + public int id { get; set; } + public string name { get; set; } + } + [Fact] public void First() { diff --git a/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs index abbcb35e..b3785bbb 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExpressionCallExtensions.cs @@ -114,6 +114,18 @@ namespace FreeSql /// /// public static IGroupConcat GroupConcat(object column) => SqlExtExtensions.GroupConcat(column); + + /// + /// 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] diff --git a/FreeSql/Extensions/LambadaExpressionExtensions.cs b/FreeSql/Extensions/LambadaExpressionExtensions.cs index 000a6d72..203010a9 100644 --- a/FreeSql/Extensions/LambadaExpressionExtensions.cs +++ b/FreeSql/Extensions/LambadaExpressionExtensions.cs @@ -233,6 +233,26 @@ namespace System.Linq.Expressions test.Visit(exp); return test.Result; } + + public static bool IsStringJoin(this MethodCallExpression exp, out MethodCallExpression joinExpArgs1Out, out LambdaExpression joinExpArgs1Args0Out) + { + 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) + { + joinExpArgs1Out = joinExpArgs1; + joinExpArgs1Args0Out = joinExpArgs1Args0; + return true; + } + joinExpArgs1Out = null; + joinExpArgs1Args0Out = null; + return false; + } } internal class NewExpressionVisitor : ExpressionVisitor diff --git a/Providers/FreeSql.Provider.SqlServer/SqlServerExpression.cs b/Providers/FreeSql.Provider.SqlServer/SqlServerExpression.cs index e0a87762..f59a05f0 100644 --- a/Providers/FreeSql.Provider.SqlServer/SqlServerExpression.cs +++ b/Providers/FreeSql.Provider.SqlServer/SqlServerExpression.cs @@ -283,6 +283,24 @@ namespace FreeSql.SqlServer return $"'+{_common.IsNull($"cast({ExpressionLambdaToSql(a, tsc)} as nvarchar(max))", "''")}+{nchar}'"; }).ToArray(); return string.Format(expArgs0, expArgs); + case "Join": + if (exp.IsStringJoin(out var joinExpArgs1, out var joinExpArgs1Args0)) + { + var newToListArgs0 = Expression.Call(joinExpArgs1.Object, joinExpArgs1.Method, + Expression.Lambda( + Expression.Call( + typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }), + Expression.Convert(joinExpArgs1Args0.Body, typeof(object)), + Expression.Convert(exp.Arguments[0], typeof(object))), + joinExpArgs1Args0.Parameters)); + var newToListSql = getExp(newToListArgs0); + if (string.IsNullOrEmpty(newToListSql) == false && newToListSql.StartsWith("(") && newToListSql.EndsWith(")")) + { + newToListSql = $"{newToListSql.Substring(0, newToListSql.Length - 1)} FOR XML PATH(''))"; + return newToListSql; + } + } + break; } } else