EF + LINQ:如何定义和利用变量(可能是Func变量)来定义Select语句的一部分?

时间:2017-08-07 23:24:58

标签: c# entity-framework linq entity-framework-6 linq-to-entities

在尝试使用变量之前(这有sql server进行计算):

IQueryable<MyEntity> query = _dbSet; // IQueryable<TEntity>
var results = query.Select(m => new MyViewModel
{
    MyCalculation = m.Column1 * m.Column2
}).ToList();

我想做什么(从Func变量或其他类型的变量动态创建我的select语句的一部分以允许这样做):

IQueryable<MyEntity> query = _dbSet; // IQueryable<TEntity>
Func<MyEntity, decimal> funcVariableAttempt = m => m.Column1 * m.Column2;
var results = query.Select(m => new MyViewModel
{
    MyCalculation = funcVariableAttempt.Invoke(m) // My foolish attempt does not work.
}).ToList();

当我尝试我想要的东西时,我得到的错误(也就是我的愚蠢尝试):

  

LINQ to Entities无法识别方法'System.Decimal Invoke(MyProject.Repository.Models.MyEntity)'方法,并且此方法无法转换为商店表达式。

如何定义和利用变量(可能是Func变量)来定义Select语句的一部分?

3 个答案:

答案 0 :(得分:1)

这是一个完全有效的问题,我之前对其进行了标记,并找到了一个适合我的解决方案。

你的Func<MyEntity, decimal>的问题在于它是一个委托,并且O / R映射器必须能够访问内部表达式(在你的情况下是两个属性的乘法)。但是这些信息被编译到代表中并永远隐藏起来。

如果你开始使用Expression<Func<MyEntity, decimal>> customCalculation,那么事情看起来更有希望,因为你有内部逻辑作为表达式树。 问题是:您无法调用表达式。 customCalculation(m)无法编译。

编译器会让你写

m => new MyViewModel { MyCalculation = customCalculation.Compile()(m) }

,但大多数O / R地图制作者都不会理解这一点。

但是你看到它至少在某种程度上包含 customCalculation lambda表达式以及它与周围表达式的关系。

从原始工作版本到此处的表达式树涉及一些表达式操作:

我们必须将customCalculation.Compile()(m)替换为Compile() d的lambda的 body ,但将lambda的参数替换为相应的表达式( s)委托调用。因此,如果customCalculationx => x.Column1 * x.Column2,则customCalculation.Compile()(m)必须替换为m.Column1 * m.Column2

这样做并不简单,因为lambda本身必须从编译器生成的闭包类的实例中挖出一个字段。

我发布了这个表达式操纵符in another similar question的实现。希望有所帮助。

有了这个,你应该能够:

var customCalculation = (Expression<Func<MyEntity, decimal>>)(x => x.Column1 * x.Column2);
var selector = Express.Prepare((Expression<Func<MyEntity, MyViewModel>>)(m => new MyViewModel { MyCalculation = customCalculation.Compile()(m) }));
var result = query.Select(selector).ToList();

答案 1 :(得分:0)

如您所知,您的funcVariableAttempt对您的数据库毫无意义,因此您必须在linq-to-object上下文中调用您的方法。例如,首先将数据作为Enumerable获取,然后调用您的方法:

var results = query.Select(m => new {
Column1= col1,
Column2= col2
}).AsEnumerable()
    .Select(m => new MyViewModel
{
    MyCalculation = Foo(m.Column1, m.Column2)
});

*注意:代码未经过测试。

答案 2 :(得分:0)

首先应调用ToList()并对内存中的结果执行Select()。

var results = query.ToList() 
 .Select(m => new MyViewModel { 
    MyCalculation = Foo(m.Column1, m.Column2)
 });

您尝试将“选择”作为查询的一部分执行。您应该只使用常规函数进行映射计算。 Lambda函数对LINQ非常有用,但在这种情况下它们并不需要。