博客園現代化建設—[Entity Framework]在LINQ查詢中指定返回的字段
解決了Entity Framework跨數據庫查詢問題,博客園現代化建設又向前邁進了一步。
在之前的一篇隨筆“ 博客園現代化建設——AutoMapper ”中曾談到,我們所遇到的應用場景是數據庫查詢返回的字段數少于實體類的屬性,而默認情況下Entity Framework根據實體類的屬性進行映射的,所以我們改用了AutoMapper。后來, 真見在評論中指出可以在LINQ通過 select new 指定查詢返回的字段,Entity Framework會根據返回的字段與實體類屬性進行映射。
我們嘗試了一下果然可以,但是,在所用的LINQ查詢代碼中有個地方不夠現代化,代碼如下:
using (BlogDbContext context = new BlogDbContext())
{
var result = (from e in context.BlogEntries
join t in context.PostTexts
on e.ID equals t.ID
where e.ID == entryId
select new
{
Title = e.Title,
Body = t.Text
})
.ToList()
.Select(e => new BlogEntry() { Title = e.Title, Body = e.Body })
.FirstOrDefault();
}
這是一個根據ID獲取文章標題與內容的查詢(文章內容存儲在一個單獨的數據庫中,所以之前我們要解決跨數據庫查詢的問題),從上面的代碼一眼就能看出兩次的select和一次ToList()顯得啰嗦。不是我們想這么啰嗦,實在是情非得已。理想中的查詢當然是下面這樣的:
using (BlogDbContext context = new BlogDbContext())
{
var result = (from e in context.BlogEntries
join t in context.PostTexts
on e.ID equals t.ID
where e.ID == entryId
select new BlogEntry()
{
Title = e.Title,
Body = t.Text
})
.FirstOrDefault();
}
但是,如果這樣,將面臨殘酷的現實:
System.NotSupportedException :
The entity or complex type 'BlogServer.Data.Provider.BlogEntry'
cannot be constructed in a LINQ to Entities query.
at System.Data.Objects.ELinq.ExpressionConverter.CheckInitializerType(Type type)
如果美好的理想遇到殘酷的現實就放棄,你永遠得不到成長;你要與現實斗爭到底,即使理想破滅,也要找到破滅的原因,也許下一個理想就成為現實。
而這篇文章寫的就是我們與這個殘酷現實斗爭的過程。
上面的錯誤信息最讓人抓狂的是:明明類型是BlogServer.Entities.BlogEntry,提示的卻是BlogServer.Data.Provider.BlogEntry,根本不存在這個類型,只是查詢代碼所在類型的命名空間是BlogServer.Data.Provider。而且代碼是編譯通過的,不知Entity Framework怎么整出這么個異類。
錯誤信息已經提示我們異常發生在System.Data.Objects.ELinq.ExpressionConverter.CheckInitializerType(Type type)中,所以要去Entity Framework的源代碼中看個究竟。哎,又要借助Reflector挖地道(強烈要求微軟開放Entity Framework的源代碼)。
挖好地道,直達CheckInitializerType(Type type),拍了張照片:

根據我們的猜測,異常是在上圖中的高亮部分拋出的(畢竟是地道,找東西很不方便,目前還沒找出證實這個猜測的確鑿代碼,請諒解)。
從上面的代碼可以看出,異常是在BuiltInTypeKind.EntityType或BuiltInTypeKind.ComplexType的條件下觸發的。由于出錯信息中提到了“complex type”,所以,一開始我們將焦點鎖定在這個條件上,在Entity Framework的源代碼中轉悠了半天,也沒找到在哪設置了這個值。
后來發現,拋出異常信息時并沒有傳遞BuiltInTypeKind,也就是說“complex type”與BuiltInTypeKind.ComplexType沒有關系。于是,我們把目光轉向了BuiltInTypeKind.EntityType,"EntityType"這個名稱讓我們產生了聯想:我們在BlogDbContext中注冊了BlogEntry這個類型。代碼如下:
public class BlogDbContext : DbContext
{
public DbSet<BlogEntry> BlogEntries { get; set; }
}
也許問題與這個有關聯?在毫無頭緒時,發現這個線索,讓人很興奮。
于是,我們新建了一個與BlogEntry具有同樣屬性的BlogEntryClone,將LINQ查詢代碼改為:
using (BlogDbContext context = new BlogDbContext())
{
var result = (from e in context.BlogEntries
join t in context.PostTexts
on e.ID equals t.ID
where e.ID == entryId
select new BlogEntryClone()
{
Title = e.Title,
Body = t.Text
})
.FirstOrDefault();
}
讓人驚喜的是:運行成功,未出現異常。猜測被證實,果然與BlogDbContext中注冊了BlogEntry有關。
驚喜之后是困惑,什么要有這個限制?而且,BlogEntryClone與BlogEntry不能有任何聯系,繼承或聚合都不行。
...
這個問題,以及之前的跨數據庫查詢問題讓我們浮想聯翩。微軟在設計Entity Framework時,似乎進行了有點脫離實際需求的假設:假設使用Entity Framework時,用不到跨數據庫查詢;假設使用Entity Framework時,很少需要指定查詢返回的字段...
Entity Framework給我們帶來了美好的希望,希望微軟不要讓大家的希望落空,希望Entity Framework能像ASP.NET MVC一樣開放(開源),希望Entity Framework能像ASP.NET MVC一樣干得漂亮...
浙公網安備 33010602011771號