我有一个IQueryable,其实体框架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<PersonP>
投影的合适语法是什么?谢谢。
P.S。 Automapper配置正确 - 我在其他地方使用它在Person和PersonP之间来回转换,即Mapper.Map<Person, PersonP>(myPersonObject)
正确返回PersonP
个对象。
编辑(更多代码):
我正在使用它来帮助函数将EF4实体POCO(PersonP)绑定到Telerik网格 - 由于它们包含循环引用(即导航属性),因此不会正确地序列化实体本身。我的代码如下所示:
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&gt;成为其中之一。我可以使用其中一个重载来表示此函数/委托调用吗?
答案 0 :(得分:11)
AutoMapper的作者现在已经解决了这个问题:http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/
他提供了一个实现,它采用所需的投影和查询,并仅在数据库中查询投影映射所需的字段。
另外,请参阅Paul Hiles的follow-on article以获得一些缓存改进。
希望这有帮助。
答案 1 :(得分:5)
使用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);
...然后它会起作用。
答案 2 :(得分:2)
如果在Select之前添加.ToList(),则可以强制映射发生在客户端(Linq to Objects)而不是服务器端(SQL)。只要确保你已经完成了过滤,所以你不要把整个表格都过来。