【发布时间】: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<Table1, Table2> 的版本,它可以把一个lambda 当作(t1, t2) => new { t1.Col1, t2.Col1 }。
在这种情况下,它会将成员表达式评估为"Table1"."Col1" 之类的东西(取决于 DBMS)——我希望,多表版本不是一成不变的,它仍然可以在没有原始 sql 的情况下实现......
【问题讨论】:
-
ServiceStack Customer Forums 中的批准是自动的,具体取决于您的电子邮件是否与具有有效许可证订阅的 ServiceStack 帐户匹配。如果您使用有效订阅的有效电子邮件地址,请发送电子邮件至 team@servicestack.net,以便我们手动验证。
标签: c# sql servicestack ormlite-servicestack