6

我有一個IQueryable,其實體框架4對象我想投影到他們的DTO等價物。一個這樣的對象'Person'是EF4類,相應的POCO PersonP是我定義的類。我正在使用Automapper在它們之間進行映射。然而,當我嘗試下面的代碼:IQueryable Lambda投影語法

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對象。

EDIT(更多的代碼):

我用這一個輔助函數來EF4實體波蘇斯(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>就是其中之一。我可以用這些重載表示這個函數/委託調用嗎?

回答

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); 

...那麼它會工作。

+0

即使使用常規方法代替AutoMapper,也會失敗。請在我的帖子中看到UPDATED2。 – Harper 2010-10-28 18:04:56

+0

查看更新。非表達式不能轉換爲SQL。 – 2010-10-28 21:18:58

+0

謝謝你的幫助。您的代碼有效(修正方法sig中的小錯字),但正如您所提到的,AutoMapper不適合此目的,並且不起作用。您能否解釋爲什麼我無法用新的ClientP {Id = ..}替換AutoMapper映射的技術原因 - 因爲兩者都返回一個ClientP對象? – Harper 2010-10-28 22:44:05

2

如果在Select之前添加.ToList(),則可以強制映射發生在客戶端(Linq to Objects)而不是服務器端(SQL)。只要確保你先完成了你的過濾,那麼你就不會把整個表格都帶過來。

+0

嗯,我實際上使用Telerik擴展方法來執行過濾/分組/排序/等,我不能將它應用於EF Queryable,因爲網格無法序列化這些實體中的循環依賴。我添加了更多的代碼。 – Harper 2010-10-28 07:13:45

+0

您可以使用匿名類型映射這些實體:http://msdn.microsoft.com/en-us/library/bb738512.aspx但是它錯過了您的Poco實體的要點。看起來真正的問題在於對單一的「ToGridModel」方法的調用。這真的需要分開,所以你可以過濾和排序,然後項目,然後做任何需要做的事情。你可以單獨應用濾波器,然後跨越邊界,然後投影並組裝GridModel? – 2010-10-28 07:56:51

+0

感謝您的建議。我將研究是否可以繞過ToGridModel()方法。 – Harper 2010-10-28 22:47:23

11

這實際上已經解決了,現在通過AutoMapper這裏筆者:http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/

他提供了取所需的投影和查詢和查詢數據庫只需要爲投影映射等領域的實現。

此外,請參閱Paul Hiles的follow-on article以獲取緩存方面的改進。

希望這會有所幫助。

+0

非常有用,謝謝。 – 2012-09-26 19:39:20

+0

官方AutoMapper文檔:https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions – 2014-02-25 10:10:10