简体   繁体   English

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

[英]ServiceStack.OrmLite: using aliases in SqlExpression for anonymous types

I am posting this question here, as I'm still waiting for approval on the ServiceStack customer forum.我在这里发布这个问题,因为我仍在等待 ServiceStack 客户论坛的批准。 It's a rather specific question, so I don't expect many can help... @mythz :)这是一个相当具体的问题,所以我不希望有很多人可以提供帮助...... @mythz :)

I'm migrating ServiceStack from v5.4 to v5.7 and there is an issue with aliases in SqlExpression .我正在将 ServiceStack 从 v5.4 迁移到 v5.7,并且SqlExpression中的别名存在问题。

I wrote a custom sql concat to get a "kind-of-csv-format" in one column, to merge data into one column, when using unions.我编写了一个自定义 sql concat 以在一列中获得“一种 csv 格式”,以便在使用联合时将数据合并到一列中。 From the SQL side, simplified version would be:从 SQL 方面来看,简化版本为:


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

In C#, using OrmLite api, I do:在 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 is my custom method, that calls the underlying IOrmLiteDialectProvider.SqlConcat() under the hood, inserting before the @delimiter between the members of anonymous types. ConcatWithSeparator是我的自定义方法,它调用底层的IOrmLiteDialectProvider.SqlConcat() ,在匿名类型成员之间的@delimiter之前插入。

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

This worked well for the v5.4, but as I noticed, in v5.7, there was a change introduced in method SqlExpression.SetAnonTypePropertyNamesForSelectExpression() ( https://github.com/ServiceStack/ServiceStack.OrmLite/blob/v5.7/src/ServiceStack.OrmLite/Expressions/SqlExpression.cs )这适用于 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);
            } 

In particular, it's about this check:特别是关于这个检查:

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

This evaluates to true also for new { x.Col1 } , as it's a short for new { Col1 = x.Col1 } which further makes the sql syntax Col1 as Col1这对于new { x.Col1 }也为 true ,因为它是new { Col1 = x.Col1 }的缩写,这进一步使 sql 语法Col1 as Col1

In my concat, it results in a wrong SQL SELECT CONCAT("Table1"."Col1" AS Col1, @delimiter, "Table1"."Col2" AS Col2) AS DATA FROM "Table1"在我的连接中,它导致错误的 SQL SELECT CONCAT("Table1"."Col1" AS Col1, @delimiter, "Table1"."Col2" AS Col2) AS DATA FROM "Table1"

So the real question is, shouldn't there be an extra check, that if it's a MemberExpression me , also me.Member.Name.= member.Name ?所以真正的问题是,不应该有一个额外的检查,如果它是一个MemberExpression me ,也是me.Member.Name.= member.Name Or maybe some extra configuration option to bypass the alias generation?或者可能有一些额外的配置选项来绕过别名生成?

Or... is there any other, easier approach to achieve, what am I trying to do?或者......还有其他更简单的方法可以实现,我想做什么? (I have to support MySQL, MSSQL, Sqlite and PostgreSQL). (我必须支持 MySQL、MSSQL、Sqlite 和 PostgreSQL)。

Update, regarding @mythz answer below更新,关于下面的@mythz 答案

Unfortunately, it's still not working as expected.不幸的是,它仍然没有按预期工作。 But I think, we are getting closer.但我想,我们越来越近了。 The converter here:转换器在这里:

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);

evaluates the expression to quoted string, so in the end there is a check "\"Col1\"" != "Col1" .将表达式计算为带引号的字符串,因此最后有一个检查"\"Col1\"" != "Col1"

Additionally, I also wrote a version of ConcatWithSeparator<Table1, Table2> that can take a lambda as (t1, t2) => new { t1.Col1, t2.Col1 } .此外,我还编写了一个版本的ConcatWithSeparator<Table1, Table2>可以将 lambda 作为(t1, t2) => new { t1.Col1, t2.Col1 } In that case, it will evaluate member expression into something like "Table1"."Col1" (depending on DBMS) - I hope, multi-table version is not a hit-in-the-wall and it still can be implemented without raw sql...在这种情况下,它会将成员表达式评估为"Table1"."Col1" (取决于 DBMS) - 我希望,多表版本不是一成不变的,它仍然可以在没有 raw 的情况下实现sql...

I've changed it to not use the alias when the alias is the same name in this commit .当别名与此提交中的别名相同时,我已将其更改为不使用别名。

This change is available from the latest v5.7.1 that's now available on MyGet .此更改可从 MyGet 上现在可用的最新 v5.7.1 中获得。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM