为什么这个LINQ表达式会破坏我的循环&转换逻辑?

时间:2012-01-25 21:13:50

标签: c# linq lambda linq-to-entities foreach

背景

ArticleService是一个为前端层提供方法以促进后端业务的类。

它的两个基本职责是在持久化数据时将ViewModels(ArticleViewModel)转换为适当的模型(Article),反之,在获取数据时将模型转换为ViewModel ...经常我创建了一个构建ViewModel对象的私有方法:

private ArticleViewModel BuildViewModel(Article a)
{
    return new ArticleViewModel { Title = a.Title /* all properties */ }
}

继续前进,ArticleService提供了一种从数据存储中获取所有文章的方法,并将它们作为ViewModel返回:public IEnumerable<ArticleViewModel> All()

调用类使用它如下:var articleViewModels = _articleService.All();

简单,对吧?

问题:

我最初懒惰地用All()循环编写foreach

private IEnumerable<ArticleViewModel> All()
{
    var viewModels = new List<ArticleViewModel>();
    foreach (var article in _db.Articles)
        viewModels.Add(BuildViewModel(article));
    return viewModels;
}

一切正常 - articleViewModels是所有视图模型的实例化列表。

接下来,我使用ReSharper将此循环转换为LINQ语句以获得性能和漂亮,然后将赋值语句与return语句结合使用。结果:

private IEnumerable<ArticleViewModel> All()
{
    return _db.Articles.Select(article => BuildViewModel(article)).ToList();
}

我调试了LINQ语句并唤醒了野兽:

  

LINQ to Entities无法识别方法'ArticleViewModel   BuildViewModel(Article)'和此方法无法转换为商店表达式。

问题 - 为什么这个LINQ语句会破坏我的代码?

注意:退回到显式声明,赋值,返回使用LINQ语句,所以我几乎可以肯定它与lambda逻辑有关。

3 个答案:

答案 0 :(得分:7)

  

问题 - 为什么这个LINQ语句会破坏我的代码?

因为LINQ to Entities正试图将BuildViewModel转换为SQL。它不知道如何,所以它悲惨地死去。

在原始版本中,您将实体从数据库流式传输到本地方框,然后使用BuildViewModel客户端进行投影。没关系。

  

所以我几乎可以肯定它与lambda逻辑有关。

不。这是因为LINQ to Entities无法将BuildViewModel转换为SQL。如果使用lambda表达式来表达投影,则无关紧要。

您可以像这样重写代码:

return _db.Articles.
          .AsEnumerable()
          .Select(article => BuildViewModel(article)).ToList();

这会导致_db.Articles被视为普通的旧枚举,然后执行投影客户端。现在,LINQ to Entities不必弄清楚如何处理BuildViewModel

答案 1 :(得分:2)

Jason的回答解释了错误,但没有[没有]列出简单修复(如果你仍然想使用LINQ)。只需在“_db.Articles”之后添加对.AsEnumerable()的调用。这将强制使用LINQ to Objects(在内存中)执行任何未来的LINQ语句,而不是尝试使用IQueryable来对数据库执行语句。

答案 2 :(得分:0)

只是您的函数BuildViewModel无法转换为原始SQL。那是。