在Entity Framework查询中使用Select子句中的函数

9
我希望对Entity Frameworks实现以下逻辑。
var items = from item in myContext
            select new {
                Value1 = TweakValue(item.Value1),
                Value2 = TweakValue(item.Value2)
            };

protected int TweakValue(int value)
{
    // Custom processing here
    return value;
}

这样做行不通,因为在select子句中调用了TweakValue()。我知道查询会转换为SQL,并且问题是TweakValue()无法转换为SQL。我的问题是最经济实惠的实现方式是什么?我需要第二个循环来转换值吗?

我仍在努力适应LINQ表达式。


1
TweakValue是做什么的?也许可以让数据库来处理。 - Aducci
2个回答

13

最简单的方法可能是将执行转移到客户端以执行转换。在这种情况下,您只需要使用:

var items = myContext.Select(item => new { item.Value1, item.Value2 })
                     .AsEnumerable()
                     .Select(item => new {
                                 Value1 = TweakValue(item.Value1),
                                 Value2 = TweakValue(item.Value2)
                             });

请注意,您不必重复使用 Value1Value2 的名称-只是这样做最容易。

如果您真的想使用查询表达式:

var query = from item in myContext
            select new { item.Value1, item.Value2 };

var items = from item in query.AsEnumerable()
            select new {
                Value1 = TweakValue(item.Value1),
                Value2 = TweakValue(item.Value2)
            };

如果您想先进行过滤,可以在调用 AsEnumerable() 之前放置过滤、排序等操作以在数据库中实现。例如:

var query = from item in myContext
            where item.Foo == bar
            orderby item.Something
            select new { item.Value1, item.Value2 };

var items = from item in query.AsEnumerable()
            select new {
                Value1 = TweakValue(item.Value1),
                Value2 = TweakValue(item.Value2)
            };

它的行为与他的查询非常不同。他的从数据库中提取两个字段,而你的则提取整行数据。 - Craig Stuntz
谢谢Jon。事实上,我的实际查询中有一个where子句。所以你最后的例子可能是我要使用的。 - Jonathan Wood
@Craig:正确。不过,创建一个匿名类型,包含我在 select 子句中需要的列,只是一件简单的事情。 - Jonathan Wood
1
值得注意的是,@CraigStuntz的评论现在已经不相关了,对于任何在2011年之后查看此答案的人来说,Jon已经编辑了他的回答。 - aspirant_sensei

5

您不需要循环,只需进行另一个投影:

var items =  myContext.Select(i => new {
                 Value1 = item.Value1,
                 Value2 = item.Value2
             })
             .AsEnumerable()
             .Select(i => new {
                 Value1 = TweakValue(item.Value1),
                 Value2 = TweakValue(item.Value2)
              });

编辑:根据TweakValue的实际功能,您可以将整个内容推送到服务器。基于您当前的示例:

public Expression<Func<Item, ItemProjection>> TweakValue()
{
    return item => new ItemProjection 
                   {
                       Value1 = item.Value1,
                       Value2 = item.Value2 + 0 // or something else L2E can understand...
                   }; 
}

现在可以像这样使用它:
var exp = TweakValue();
var items =  myContext.Select(exp);

注意,我将exp存储在变量中,以便L2E不会尝试直接在查询中调用TweakValue,否则将失败。
当然,这仅适用于TweakValue执行的操作是L2E可以执行的。

@Jon,同意,前提是它们只是简单的属性引用。由于我的EF查询可能有50%以上比较复杂,所以我不太倾向于这样做,因为这会在后期创建维护问题--当一个给定的查询需要超过单个对象属性的引用时,我必须重写整个查询来使用显式命名的属性。 - Craig Stuntz
@Craig:为什么?毕竟你只需要重写那些需要执行不同任务的部分即可。 - Jon Skeet
@Jon:因为我的大多数带有投影的L2E查询最终都变得非常复杂。换句话说,“需要执行不同操作的位”占据了查询的大部分内容。 - Craig Stuntz
@Craig:嗯,那不是你说的意思...你给人的印象是只要查询的任何部分需要更改,你就会立即重写整个查询,但你肯定不需要这样做。为什么不在可以使用它的地方使用它,在不需要使用它的地方不使用它呢?你声称它会产生维护问题,但我看不出为什么...这不像是有兼容性问题。 - Jon Skeet
@Jon:我同意没有兼容性问题。但对我来说,这有点像我总是在if中使用{}一样:虽然在主体中只有一个语句不是必需的,但由于我知道大多数if最终都会有>1个语句,即使它们一开始并不是这样,我默认这样做。我接受其他人做出不同的选择,但我认为“更简洁”的语法实际上并没有节省任何时间,所以我选择不使用它,主要是这个原因。 - Craig Stuntz
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接