Linq 表达式树 - 连接值数组
Linq Expression Tree - Concatenate Array of values
我目前正在使用 .net 核心 (vnext) 解决方案,它使用 linq 和 entity framework 核心。
我们正在使用 Microsoft.Linq.Translations 将计算列定义存储为 lambda 表达式,以便在需要时可以将它们注入到我们的语句中。计算定义的示例如下:
private static readonly CompiledExpression<Model, string> fullNameExpression =
aTranslation<Model>.Expression(e => e.FullName, e => $"{e.FirstName} {e.LastName} ({e.Code})");
然后 consume/execute 它:
var list = context.Set<Model>().Where(e => e.FullName.Contains("Bob")).WithTranslations();
这一切 "techincally" 工作正常....因为正确的结果被绑定回实体模型,但是工作是在内存中完成的,而不是在 [=65= 中构建查询].这是一个问题,因为我们的数据模型可能最终包含数百万行数据,并且在内存中执行此操作会很麻烦。
这就是我们目前的结果:
.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) {
.Call (.Call System.String.Format(
"{0} {1} ({2})",
$e.FirstName,
$e.LastName,
$e.Code)).Contains("Bob")
}
正如我所说,这可行,但会导致任务在内存中而不是 SQL 中执行。生成的查询类似于:
SELECT FirstName, LastName, Code FROM Model
所以,如果我将计算定义更改为以下内容(请注意,我只是手动构建一个字符串,而不是使用 string.format:
private static readonly CompiledExpression<Model, string> fullNameExpression =
aTranslation<Model>.Expression(e => e.FullName, e => e.FirstName + " " + e.LastName + "(" + e.Code + ")");
这会生成如下所示的 lambda 表达式:
.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) {
.Call ($e.FirstName + " " + $e.LastName + " (" + $e.Code + ")").Contains("Bob")}
这将起作用并正确构建 SQL 查询。
SELECT * FROM Model WHERE (FirstName + ' ' + LastName + '(' + Code + ')') LIKE '%bob%'
因此,除了改变计算定义的方式 written/defined,我正在尝试编写一个层,该层采用使用字符串格式 linq 表达式编写的定义,并将该表达式替换为第二种类型(例如字符串连接)
我已经开始通过截取字符串格式表达式并构建 list/array 成员和常量表达式来编写这一层,即
名,
" ",
姓,
“(”
等....
我尝试通过循环遍历值并评估左右表达式来使用字符串连接方法,但这最终导致其他不起作用:
.Call System.String.Concat(
.Call System.String.Concat(
.Call System.String.Concat(
.Call System.String.Concat(
$e.FirstName,
" "),
$e.LastName),
" ("),
$e.Code)
现在,我不知道如何转换该表达式列表以生成这种表达式:
.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) {
.Call ($e.FirstName + " " + $e.LastName + " (" + $e.Code + ")").Contains("Bob")}
如有任何帮助,我们将不胜感激。
----------------编辑------------
到目前为止已经尝试过代码。为了简洁起见,我省略了 "concatArgs" 变量的构建。它只是一个包含 MemberExpression 和 ConstantExpression 类型的列表。
Expression expr = GenerateStringConcat(concatArgs[0], concatArgs[1]);
for(int i = 2; i < concatArgs.Count; i++)
expr = GenerateStringConcat(expr, concatArgs[i]);
被调用的方法如下所示
private Expression GenerateStringConcat(Expression left, Expression right) {
return Expression.Call(
null,
typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
new[] { left, right });
}
好的,我明白了!
@usr 的评论促使我更深入地研究语法树,看看代码是如何被编译下来的。我在 "View -> Other Windows -> Syntax Visualizer"
下使用了 VS2015 内置的语法可视化工具
当分解所需结果的标准 linq 表达式时,这表明在幕后将其编译成看起来很奇怪的 BinaryOperator...
最初我尝试将我的 string.concat 调用替换为:
BinaryExpression.Add(concatArgs[0], concatArgs[1])
但是,这导致了以下错误:
The binary operator Add is not defined for the types 'System.String'
and 'System.String'
这让我想到了这个 article/post:
"The binary operator Add is not defined for the types 'System.String' and 'System.String'." -- Really?
建议过载:
https://msdn.microsoft.com/en-us/library/bb339231%28v=vs.110%29.aspx
基本上,在幕后这一切最终都会被转换成一个 string.concat 调用,但是使用 BinaryExpression.Add 方法似乎允许 linq 在运行时解决这个问题并且只使用一个调用到 string.concat?也许 "params object[]"?
无论如何,将我的 GenerateStringConcat 方法更改为以下内容已经成功:
private Expression GenerateStringConcat(Expression left, Expression right)
{
return BinaryExpression.Add(left, right, typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }));
}
我目前正在使用 .net 核心 (vnext) 解决方案,它使用 linq 和 entity framework 核心。
我们正在使用 Microsoft.Linq.Translations 将计算列定义存储为 lambda 表达式,以便在需要时可以将它们注入到我们的语句中。计算定义的示例如下:
private static readonly CompiledExpression<Model, string> fullNameExpression =
aTranslation<Model>.Expression(e => e.FullName, e => $"{e.FirstName} {e.LastName} ({e.Code})");
然后 consume/execute 它:
var list = context.Set<Model>().Where(e => e.FullName.Contains("Bob")).WithTranslations();
这一切 "techincally" 工作正常....因为正确的结果被绑定回实体模型,但是工作是在内存中完成的,而不是在 [=65= 中构建查询].这是一个问题,因为我们的数据模型可能最终包含数百万行数据,并且在内存中执行此操作会很麻烦。
这就是我们目前的结果:
.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) {
.Call (.Call System.String.Format(
"{0} {1} ({2})",
$e.FirstName,
$e.LastName,
$e.Code)).Contains("Bob")
}
正如我所说,这可行,但会导致任务在内存中而不是 SQL 中执行。生成的查询类似于:
SELECT FirstName, LastName, Code FROM Model
所以,如果我将计算定义更改为以下内容(请注意,我只是手动构建一个字符串,而不是使用 string.format:
private static readonly CompiledExpression<Model, string> fullNameExpression =
aTranslation<Model>.Expression(e => e.FullName, e => e.FirstName + " " + e.LastName + "(" + e.Code + ")");
这会生成如下所示的 lambda 表达式:
.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) {
.Call ($e.FirstName + " " + $e.LastName + " (" + $e.Code + ")").Contains("Bob")}
这将起作用并正确构建 SQL 查询。
SELECT * FROM Model WHERE (FirstName + ' ' + LastName + '(' + Code + ')') LIKE '%bob%'
因此,除了改变计算定义的方式 written/defined,我正在尝试编写一个层,该层采用使用字符串格式 linq 表达式编写的定义,并将该表达式替换为第二种类型(例如字符串连接)
我已经开始通过截取字符串格式表达式并构建 list/array 成员和常量表达式来编写这一层,即 名, " ", 姓, “(” 等....
我尝试通过循环遍历值并评估左右表达式来使用字符串连接方法,但这最终导致其他不起作用:
.Call System.String.Concat(
.Call System.String.Concat(
.Call System.String.Concat(
.Call System.String.Concat(
$e.FirstName,
" "),
$e.LastName),
" ("),
$e.Code)
现在,我不知道如何转换该表达式列表以生成这种表达式:
.Lambda #Lambda1<System.Func`2[Model,System.Boolean]>(Model $e) {
.Call ($e.FirstName + " " + $e.LastName + " (" + $e.Code + ")").Contains("Bob")}
如有任何帮助,我们将不胜感激。
----------------编辑------------
到目前为止已经尝试过代码。为了简洁起见,我省略了 "concatArgs" 变量的构建。它只是一个包含 MemberExpression 和 ConstantExpression 类型的列表。
Expression expr = GenerateStringConcat(concatArgs[0], concatArgs[1]);
for(int i = 2; i < concatArgs.Count; i++)
expr = GenerateStringConcat(expr, concatArgs[i]);
被调用的方法如下所示
private Expression GenerateStringConcat(Expression left, Expression right) {
return Expression.Call(
null,
typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
new[] { left, right });
}
好的,我明白了!
@usr 的评论促使我更深入地研究语法树,看看代码是如何被编译下来的。我在 "View -> Other Windows -> Syntax Visualizer"
下使用了 VS2015 内置的语法可视化工具当分解所需结果的标准 linq 表达式时,这表明在幕后将其编译成看起来很奇怪的 BinaryOperator...
最初我尝试将我的 string.concat 调用替换为:
BinaryExpression.Add(concatArgs[0], concatArgs[1])
但是,这导致了以下错误:
The binary operator Add is not defined for the types 'System.String' and 'System.String'
这让我想到了这个 article/post:
"The binary operator Add is not defined for the types 'System.String' and 'System.String'." -- Really?
建议过载:
https://msdn.microsoft.com/en-us/library/bb339231%28v=vs.110%29.aspx
基本上,在幕后这一切最终都会被转换成一个 string.concat 调用,但是使用 BinaryExpression.Add 方法似乎允许 linq 在运行时解决这个问题并且只使用一个调用到 string.concat?也许 "params object[]"?
无论如何,将我的 GenerateStringConcat 方法更改为以下内容已经成功:
private Expression GenerateStringConcat(Expression left, Expression right)
{
return BinaryExpression.Add(left, right, typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }));
}