【问题标题】:ServiceStack.OrmLite: using aliases in SqlExpression for anonymous typesServiceStack.OrmLite:在 SqlExpression 中为匿名类型使用别名
【发布时间】:2019-11-21 22:55:12
【问题描述】:

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

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

我编写了一个自定义 sql concat 以在一列中获取“一种 csv 格式”,以便在使用联合时将数据合并到一列中。 从 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 中,methodSqlExpression.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 } 的计算结果也为 true,因为它是 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&lt;Table1, Table2&gt; 的版本,它可以把一个lambda 当作(t1, t2) =&gt; new { t1.Col1, t2.Col1 }。 在这种情况下,它会将成员表达式评估为"Table1"."Col1" 之类的东西(取决于 DBMS)——我希望,多表版本不是一成不变的,它仍然可以在没有原始 sql 的情况下实现......

【问题讨论】:

  • ServiceStack Customer Forums 中的批准是自动的,具体取决于您的电子邮件是否与具有有效许可证订阅的 ServiceStack 帐户匹配。如果您使用有效订阅的有效电子邮件地址,请发送电子邮件至 team@servicestack.net,以便我们手动验证。

标签: c# sql servicestack ormlite-servicestack


【解决方案1】:

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

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

【讨论】:

  • 速度很快!我将更新放在我原来的问题中,因为我无法将它放在评论中。
  • @l3m00r 检查现在在this commit 中引用不可知,您需要clear your NuGet packages cache 下载最新的v5.7.1 包。
  • 太棒了!就像一张桌子的魅力一样。但是,正如我所料,在 JOIN 中使用时,评估的 MemberExpression 将以类型/表名称为前缀:"Table1"."Col1",因此在这种情况下剥离引号将无济于事。
  • @l3m00r 现在应该可以从 this commit 获得支持,MyGet 上的最新 v5.7.1 也可以提供此功能
  • 谢谢! SQL 查询现在正在运行,没有问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多