C# - LINQ to Entities 查詢中無法建構實體或複雜類型

  • 4348
  • 0
  • C#
  • 2020-07-27

在ORM盛行的時代,很多人都學會了使用LINQ來進行資料庫的撈取與操作

但是LINQ在操作時,有些地方需要額外注意的。否則就會產上如標題上所說的:

LINQ to Entities 查詢中無法建構實體或複雜類型

因此今天來跟大家談談,關於這問題,以及該如何解決。

一般來說,我們在撰寫LINQ撈取資料庫的時候,我們會這樣寫:

var data = db.Store.select(x=> {

   x.StoreId,
   x.StoreName
 
}).ToList();

但是筆者在撰寫Window Form的時候,由於想要將資料庫撈取出來的值,自動Binding到Combox上

並且可以針對這個資料集合做後續的操作,好讓我的Combox資料可以即時變動。

因此腦筋一轉,想著在Select的時候,為什麼總是建立一個匿名類別來儲存,而不是一開始就指定為一個強型別來做操作。

於是我在select後面,加上了由Entity映射所產生的類別:

List<Store> list = new List<Store>();

list = db.Store.select(x=> new Store{

   Store.Id = x.StoreId,
   Store.Name = x.Store.Name
}).ToList();

結果這麼一寫,發現程式居然報錯了,原因為:

LINQ to Entities 查詢中無法建構實體或複雜類型

此時筆者開始好奇,是什麼樣的原因會導致這樣的錯誤發生?

後來在某一篇的stackoverflow 當中,找到了解答:

Well, mapped entities in EF basically represent database tables. If you project onto a mapped entity, what you basically do is partially load an entity, which is not a valid state. EF won't have any clue how to e.g. handle an update of such an entity in the future (the default behaviour would be probably overwriting the non-loaded fields with nulls or whatever you'll have in your object). This would be a dangerous operation, since you would risk losing some of your data in the DB, therefore it is not allowed to partially load entities (or project onto mapped entities) in EF

主要原因是說,由Entity映射產生出來的類別,是用來表示定義資料庫的欄位,並不是一個有效的實體

也就是說,裡面的定義是為了資料庫而生,因此裡面有著許多定義資料庫的欄位、屬性 等等

而這些東西,並沒辦法轉換為SQL去執行,因此如果後面採用映射出來的類別,可能會造成資料庫的數據遺失等問題存在。

那...既然如此,我們應該如何解決這個問題?

這邊筆者提出兩種方式來解決:

  1. 利用匿名類別將資料從資料庫讀取並轉換為LIST,接著再下一次Select,將LIST當中的內容讀出並且轉換為強型別
  2. 利用ViewModel將內容讀取出來

首先第一種方式筆者個人比較不推薦,因為寫法來說相較並不直觀:

List<Store> list = new List<Store>();

list = db.Store.select(x=> new {

   x.StoreId,
   x.StoreName
}).ToList().select(x => new Store {

   StoreId = x.StoreId,
   StoreName = x.StoreName,
}).ToList();

利用以上的方式,等於我們相同的程式碼要再寫一次。雖然邏輯上來說完全不一樣,但是對開發者來說,卻要select兩次,相當不直觀。

因此筆者這邊推薦採用ViewModel的方式來去接值。

我們先建立一個ViewModel,裡面的欄位就是你想從資料庫當中撈取的欄位。

接著我們只需要這樣做就可以了:

List<StoreViewModel> list = new List<StoreViewModel>();

list = db.Store.select(x=> new StoreViewModel{

   StoreId = x.StoreId,
   StoreName = x.StoreName,
}).ToList();

透過這樣的方式,我們將資料庫撈出來的內容 Binding到我們的ViewModel上,就可以解決這樣的問題了。

以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教:)

有任何家教、案子 或技術相關問題 請都歡迎聯繫我

http://www.zhenghui.idv.tw/