【问题标题】:IQueryable Lambda Projection SyntaxIQueryable Lambda 投影语法
【发布时间】:2010-10-28 07:04:59
【问题描述】:

我有一个 IQueryable,我想将其 Entity Framework 4 对象投影到它们的 DTO 等效项。一个这样的对象“Person”是一个 EF4 类,相应的 POCO PersonP 是我定义的一个类。我正在使用 Automapper 在它们之间进行映射。但是,当我尝试以下代码时:

IQueryable<Person> originalModel = _repo.QueryAll();
IQueryable<PersonP> projection = originalModel.Select(e => Mapper.Map<Person, PersonP>(e));

投影在运行时会产生这个错误:

LINQ to Entities does not recognize the method 'TestSite.Models.PersonP Map[Person,PersonP](TestSite.DataLayer.Model.Person)' method, and this method cannot be translated into a store expression.

使用 Automapper 创建IQueryable&lt;PersonP&gt; 投影的适当语法是什么?谢谢。

附: Automapper 配置正确 - 我在其他地方使用它在 Person 和 PersonP 之间来回转换,即 Mapper.Map&lt;Person, PersonP&gt;(myPersonObject) 正确返回 PersonP 对象。

编辑(更多代码):

我使用它作为辅助函数,将 EF4 实体 POCO (PersonP) 绑定到 Telerik Grid - 因为它们包含循环引用(即导航属性),所以实体本身不会正确序列化。我的代码如下所示:

public static GridModel GetGridModel<TEntity, TPoco>(IRepository<TEntity> repo, GridState gridState) where TEntity : EntityObject
{
 var originalModel = repo.QueryAll().ToGridModel(gridState);
 var projection = originalModel.Select(e => Mapper.Map<TEntity, TPoco>(e));


 return projection.ToGridModel(gridState); // applies filters, sorts, pages, etc...
}

.ToGridModel 方法是IQueryable 的扩展方法,它返回一个我无法可靠解析的复杂对象 - 所以这让我相信我必须在完成对 POCO 的投影后执行过滤。

更新 2:

为了简化事情,我做了一个像这样的非泛型方法:

public static GridModel GetGridModel2(IRepository<Client> repo, GridState gridState)
{
 IQueryable<Client> originalModel = repo.QueryAll();
 IQueryable<ClientP> projection = originalModel.Select(c => ClientToClientP(c));

 return projection.ToGridModel(gridState);
}

private static ClientP ClientToClientP(Client c)
{
 return new ClientP { Id = c.Id, FirstName = c.FirstName };
}

此代码在创建投影时也会失败。我注意到 IQueryable.Select() 有多个重载: Expression> 是其中之一。我可以使用这些重载之一来表示这个函数/委托调用吗?

【问题讨论】:

    标签: linq-to-entities lambda linq-to-objects iqueryable projection


    【解决方案1】:

    AutoMapper 的作者现在已经解决了这个问题:http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/

    他提供了一个实现,该实现采用所需的投影和查询,并仅在数据库中查询投影映射所需的字段。

    另外,请参阅 Paul Hiles 的 follow-on article 了解一些缓存改进。

    希望这会有所帮助。

    【讨论】:

    【解决方案2】:

    使用 Automapper 创建 IQueryable 投影的适当语法是什么?

    没有。 Automapper doesn't do this。它不适合这项工作。

    可以创建一个类似于 Automapper 的工具来为查询投影做类似的事情。我过去曾考虑过它,但总是得出结论,使用它的代码比投影的可读性差。我不想优化代码编写时间而不是代码阅读时间。

    您更新的代码不起作用,因为它不是表达式。如果你这样做:

    private static Expression<Func<Client, ClientP>> ClientP ClientToClientP()
    {
        return c => new ClientP { Id = c.Id, FirstName = c.FirstName };
    }
    

    ...然后:

    IQueryable<Client> originalModel = repo.QueryAll();
    Expression<Func<Client, ClientP>> exp = ClientToClientP();
    IQueryable<ClientP> projection = originalModel.Select(exp);
    

    ...然后它会工作。

    【讨论】:

    • 即使使用常规方法而不是 AutoMapper 也会失败。请参阅我的帖子中的 UPDATED2。
    • 感谢您的帮助。您的代码有效(纠正了方法 sig 中的小错字),但正如您所提到的,AutoMapper 不适合此目的并且不起作用。您能否解释一下为什么我不能用新的 ClientP { Id = .. } 替换 AutoMapper 映射的技术原因 - 因为两者都返回一个 ClientP 对象?
    • 由于同样的原因您的函数不起作用:因为编译的函数无法转换为 SQL;只有表达式可以。
    • 进一步说明:C# 编译器将观察到您的新方法返回一个Expression&lt;Function&lt;…,因此会将new 音译为与SQL 兼容的表达式。但是Mapper.Map 调用会变成表达式主体中的Invoke,并且不会转换为 SQL。
    • 这个答案不再正确(尽管它在给出的时候是正确的)。 AutoMapper 现在提供此功能:github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions
    【解决方案3】:

    如果您在 Select 之前添加 .ToList() ,您可以强制映射发生在客户端(Linq to Objects)而不是服务器端(SQL)。只需确保您已先完成过滤,这样您就不会把整个表格都带过来。

    【讨论】:

    • 好吧,我实际上是在使用 Telerik 扩展方法来执行过滤/分组/排序/等,我无法将其应用于 EF Queryable,因为网格无法序列化这些实体中的循环依赖项。我添加了更多代码。
    • 您可以使用匿名类型映射实体:msdn.microsoft.com/en-us/library/bb738512.aspx 但这错过了您的 Poco 实体的重点。看起来真正的问题是对整体 ToGridModel 方法的调用。这确实需要拆分,以便您可以过滤和排序,然后进行项目,然后做它需要做的任何其他事情。你能单独应用它的过滤器,然后越界,然后投影和组装 GridModel 吗?
    • 感谢您的建议。我会调查是否可以绕过 ToGridModel() 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 1970-01-01
    • 1970-01-01
    • 2018-05-03
    相关资源
    最近更新 更多