ServiceStack.OrmLite: 在 SqlExpression 中为匿名类型使用别名

ServiceStack.OrmLite: using aliases in SqlExpression for anonymous types

我在这里发布这个问题,因为我仍在 ServiceStack 客户论坛上等待批准。 这是一个相当具体的问题,所以我不希望很多人能提供帮助... @mythz :)

我正在将 ServiceStack 从 v5.4 迁移到 v5.7,SqlExpression 中的别名存在问题。

我写了一个自定义 sql concat 来在一列中得到一个 "kind-of-csv-format",以便在使用联合时将数据合并到一列中。 从 SQL 方面来看,简化版本为:


SELECT CONCAT(Col1, ',', Col2) as Data FROM Table1
UNION ALL
SELECT CONCAT(Col3, ',', Col4, ',', Col5) as Data FROM Table2

在 C# 中,使用 OrmLite api,我这样做:

                var q1 = db.From<Table1>();
                q1.Select(x => new
                {
                    Data = Sql.Custom(q1.ConcatWithSeparator("@delimiter", y => new { y.Col1, y.Col2 }))
                });

ConcatWithSeparator 是我的自定义方法,它在后台调用底层 IOrmLiteDialectProvider.SqlConcat(),在匿名类型成员之间的 @delimiter 之前插入。

这给了我: SELECT CONCAT("Table1"."Col1", @delimiter, "Table1"."Col2") AS DATA FROM "Table1"

这适用于 v5.4,但正如我所注意到的,在 v5.7 中,方法中引入了一个更改SqlExpression.SetAnonTypePropertyNamesForSelectExpression() (https://github.com/ServiceStack/ServiceStack.OrmLite/blob/v5.7/src/ServiceStack.OrmLite/Expressions/SqlExpression.cs)

            if (arg is ConditionalExpression ce ||                           // new { Alias = x.Value > 1 ? 1 : x.Value }
                arg is BinaryExpression      be ||                           // new { Alias = x.First + " " + x.Last }
                arg is MemberExpression      me ||                           // new { Alias = DateTime.UtcNow }
                arg is ConstantExpression ct)                                // new { Alias = 1 }
            {
                IOrmLiteConverter converter;
                var strExpr = !(expr is PartialSqlString) && (converter = DialectProvider.GetConverterBestMatch(expr.GetType())) != null
                    ? converter.ToQuotedString(expr.GetType(), expr)
                    : expr.ToString();

                return new PartialSqlString(strExpr + " AS " + member.Name);
            } 

特别是关于这张支票:

            if (
                ...
                arg is MemberExpression      me ||                           // new { Alias = DateTime.UtcNow }
                ...
                ) 

这对 new { x.Col1 } 的计算结果也为真,因为它是 new { Col1 = x.Col1 } 的缩写,这进一步使 sql 语法 Col1 as Col1

在我的 concat 中,结果是错误的 SQL SELECT CONCAT("Table1"."Col1" AS Col1, @delimiter, "Table1"."Col2" AS Col2) AS DATA FROM "Table1"

所以真正的问题是,是否应该进行额外检查,如果它是 MemberExpression me,那么是否还 me.Member.Name != member.Name?或者可能有一些额外的配置选项来绕过别名生成?

或者...有没有其他更容易实现的方法,我想做什么? (我必须支持 MySQL、MSSQL、Sqlite 和 PostgreSQL)。

更新,关于@mythz 下面的回答

不幸的是,它仍然没有按预期工作。但我认为,我们越来越近了。 转换器在这里:

IOrmLiteConverter converter;
var strExpr = !(expr is PartialSqlString) && (converter = DialectProvider.GetConverterBestMatch(expr.GetType())) != null
    ? converter.ToQuotedString(expr.GetType(), expr)
    : expr.ToString();

return new PartialSqlString(strExpr != member.Name
    ? strExpr + " AS " + member.Name
    : strExpr);

将表达式计算为带引号的字符串,所以最后有一个检查 "\"Col1\"" != "Col1".

此外,我还写了一个ConcatWithSeparator<Table1, Table2>的版本,可以将lambda作为(t1, t2) => new { t1.Col1, t2.Col1 }。 在那种情况下,它会将成员表达式评估为 "Table1"."Col1" 之类的东西(取决于 DBMS)——我希望 multi-table 版本不是一蹴而就的,它仍然可以实现没有原始 sql...

我已将其更改为当别名与 this commit 中的名称相同时不使用别名。

此更改适用于现在 available on MyGet 的最新 v5.7.1。