【发布时间】:2016-06-26 00:46:37
【问题描述】:
我目前正在使用 .net 核心 (vnext) 解决方案,该解决方案使用 linq 和实体框架核心。
我们使用 Microsoft.Linq.Translations 将计算列定义存储为 lambda 表达式,以便在需要时将它们注入到我们的语句中。计算定义的示例如下:
private static readonly CompiledExpression<Model, string> fullNameExpression =
aTranslation<Model>.Expression(e => e.FullName, e => $"{e.FirstName} {e.LastName} ({e.Code})");
并使用/执行它:
var list = context.Set<Model>().Where(e => e.FullName.Contains("Bob")).WithTranslations();
这一切“技术上”都可以正常工作......因为正确的结果被绑定回实体模型,但是工作是在内存中完成的,而不是在 SQL 中构建查询。这是一个问题,因为我们的数据模型最终可能包含数百万行数据,并且在内存中执行此操作会很麻烦。
这就是我们目前的结果:
.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%'
因此,除了更改计算定义的写入/定义方式之外,我正在尝试编写一个层,该层采用使用字符串格式 linq 表达式编写的定义并将该表达式替换为第二种类型(如字符串连接)
我已经开始通过截取字符串格式表达式并构建成员和常量表达式的列表/数组来编写这一层,即 名, " ", 姓, " (" 等等……
我尝试通过遍历值并评估左右表达式来使用字符串连接方法,但这最终会导致其他不起作用的东西:
.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 类型的 List。
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 });
}
【问题讨论】:
-
在有效的查询中手动编写表达式。然后,反编译程序集(或在线使用 Roslyn)查看 C# 编译器生成的表达式树。这是您可以使用的模板。
-
发布您尝试过的代码,以便我们尝试调整它。
标签: entity-framework linq linq-to-sql lambda linq-to-entities