2012-09-12 114 views
2

所以我最近發現,你可以強制實體框架不要將你的投影轉化爲SQL,只需要指定Func<T, TResult>擴展爲.Select()而不是表達式。當您想要轉換查詢的數據時,這很有用,但這種轉換應該發生在您的代碼而不是數據庫中。此擴展方法是否有效地實現了我的IQueryable?

例如,使用EF5新的枚舉支持,並試圖項目,要在DTO的字符串屬性,而這會失敗時:

results.Select(r => new Dto { Status = r.Status.ToString() }) 

這會工作:

results.Select(new Func<Record, Dto>(r => new Dto { Status = r.Status.ToString() })); 

因爲在第一個(表達式)情況下,EF無法弄清楚如何將Status.ToString()轉換爲SQL數據庫可以執行的內容,但根據this article Func謂詞不會被轉換。

一旦我有這個工作,這是沒有太大的飛躍創造了下面的擴展方法:

public static IQueryable<T> Materialize<T>(this IQueryable<T> q) 
{ 
    return q.Select(new Func<T, T>(t => t)).AsQueryable(); 
} 

所以我的問題是 - 是否有任何陷阱我應該警惕使用這種時候?是否會對性能產生影響 - 要麼將此無所作爲投影插入到查詢管道中,要麼導致EF不將.Where()子句發送到服務器,從而通過線路發送所有結果?

意圖是仍然使用.Where()方法來篩選服務器上的結果,但隨後.Select()使用前.Materialize()使提供者不試着翻譯投射到SQL Server:

return Context.Record 
    .Where(r => // Some filter to limit results) 
    .Materialize() 
    .Select(r => // Some projection to DTO, etc.); 
+0

看起來不錯。應在哪裏發送數據庫。但是,爲什麼不簡單使用'ToList()'而不是'Materialize'? –

回答

2

只需用AsEnumerable也應該這樣做:

return Context.Record 
       .Where(r => // Some filter to limit results) 
       .AsEnumerable() // All extension methods now accept Func instead of Expression 
       .Select(r => // Some projection to DTO, etc.); 

也有在你的Materialise的方法,回去IQueryable沒有理由,因爲我t不是真的IQueryable翻譯成另一個查詢了。這只是IEnumerable

在性能方面你應該沒問題。實現之前的所有內容都將在數據庫中進行評估,並在代碼實現之後進行評估。此外,在你和我的例子中,查詢仍然延遲執行 - 直到列舉查詢時才執行。

0

雖然存在一個問題:獲取到客戶端的列數。在第一種情況下,它會像select Status from Record和另一個select Status, field2, field3, field4 from Record

相關問題