如何在EF查询中用函数参数化选择器

本文关键字:函数 参数 选择器 EF 查询 | 更新日期: 2023-09-27 18:00:07

我有一个投影函数,我将它传递给IQueryable<>.Select()方法:

private static Expression<Func<VendorPrice, PriceItem>> GetPriceSelector(){
    return e => new PriceItem {
        Id = e.Id,
        Price = Math.Round(e.Price, 4)
    };
}

一切都很好,但我想这样参数化它:

private static Expression<Func<VendorPrice, PriceItem>> GetPriceSelector(Func<VendorPrice, decimal> formula){
    return e => new PriceItem {
        Id = e.Id,
        Price = formula(e)
    };
}

这样我就可以称之为

prices.Select(GetPriceSelector(e => Math.Round(e.Price, 4)))

不幸的是,EF对此颇有怨言:

LINQ to中不支持LINQ表达式节点类型"Invoke"实体

如何重写代码让EF开心?

如何在EF查询中用函数参数化选择器

首先,GetPriceSelector方法需要接受一个表达式,而不是函数。不同之处在于,表达式是作为数据的代码,因此可以转换为SQL,而函数是编译代码,因此无法转换为SQL。

接下来,您需要一种方法来合并这两个表达式。手动操作很难。幸运的是,有一个名为LINQKit的库可以做到这一点。以下是使用LINQKit解决问题的方法:

private static Expression<Func<VendorPrice, PriceItem>> GetPriceSelector(
    Expression<Func<VendorPrice, decimal>> formula)
{
    Expression<Func<VendorPrice, PriceItem>> expression = e => new PriceItem
    {
        Id = e.Id,
        Price = formula.Invoke(e) //use the forumla expression here
    };
    return expression.Expand(); //This causes formula.Invoke(e) to be converted 
                                //to something like Math.Round(e.Price, 4)
}