標籤:
前言
一般我們在開法 ASP.NET Web API 時,如果是使用 Entity Framework 技術來操作資料庫的話,當兩個 Entity 之間包含導覽屬性(Navigation Property)時,而當我們輸出的格式為 JSON 對象時,會出現一個例外,錯誤訊息為:「‘ObjectContent`1‘ 類型無法序列化內容類型 ‘application/json; charset=utf-8‘ 的回應主體。」,而小弟參考了 Will 保哥以及 Bruce 兩位前輩的文章後,整理出兩種小弟覺得比較可行的替代與解決方案。
瞭解問題
這張圖裡包含了兩張資料表 Orders 與 Order_Details ,兩者之間存在著一對多的關係,而預設 Entity Framework 會自動幫我們的關聯資料表加入導覽屬性(Navigation Property),接著我們往下一張圖看下去:
public IEnumerable<Orders> GetOrders() { return db.Orders; }
這段程式碼為 ValuesController 裡的一個 Function ,當我們請求時會返回 Orders 所有資料,但當我們輸入網址 /api/Values/ 請求時卻發生了這樣的錯誤:
這個問題發生的原因為,當我們請求某個特定的 Enity 時會取出該 Entity 的所有屬性內容,當然包誇了導覽屬性的資料,而究竟這個問題如何照成呢?以目前的案例來看,當我們取得 Orders 的資料時也會一併取得其導覽屬性,也就是 Order_Details 的所有資料,而在 Order_Details 裡也包含著 Orders 的導覽屬性,所以又會在去取得 Orders 的資料....,這樣兩個實體之間不停的往返就會造成了無限迴圈,也是我們前面所說的迴圈參考的錯誤。
如何解決
方法一:
在開發 ASP.NET MVC 中,時常會用到部分類別(Partail Class)來為我們的資料域位加上驗證屬性,所以利用此特性來解決我們的問題,首先在 Model 資料夾底下新增兩個檔案分別為:OrdersMetadata.cs 、Order_DetailsMetadata.cs
view sourceprint?
01.
OrdersMetadata.cs
02.
03.
[MetadataType(
typeof
(OrderMD))]
04.
public
partial
class
Order
05.
{
06.
public
class
OrderMD
07.
{
08.
[JsonIgnore()]
// 需引用 using Newtonsoft.Json;
09.
public
virtual
ICollection<Order_details> Order_Details {
get
;
set
; }
10.
}
11.
}Order_DetailsMetadata.cs
12.
13.
[MetadataType(
typeof
(Order_DetailsMD))]
14.
public
partial
class
Order_Details
15.
{
16.
public
class
Order_DetailsMD
17.
{
18.
[JsonIgnore()]
// 需引用 using Newtonsoft.Json;
19.
public
virtual
Orders Orders {
get
;
set
; }
20.
}
21.
}
這邊我們在在對應的導覽屬性上都加上 「JsonIgnore」的屬性,來防止迴圈參考的問題發生,記得是有關聯的兩邊都要加上「JsonIgnore」的屬性。
方法二:
另外一種方法則是利用我們在開發 ASP.NET MVC 時常用到的 ViewModel 的概念,針對特定的頁面或請求只返回特定的資料,所以這邊我們能透過 DTO 方式來解決我們問題,首先我們先在 Model 底下新增一個 DTO.cs 檔案: www.it165.net
DTO.cs
view sourceprint?
01.
public
class
OrderDTO
02.
{
03.
public
int
OrderID {
get
;
set
; }
04.
public
string
CustomerID {
get
;
set
; }
05.
public
int
? EmployeeID {
get
;
set
; }
06.
public
DateTime? OrderDate {
get
;
set
; }
07.
public
List<Order_detailsDTO> Order_Detail {
get
;
set
; }
08.
}
09.
public
class
Order_DetailsDTO
10.
{
11.
public
int
OrderID {
get
;
set
; }
12.
public
decimal
UnitPrice {
get
;
set
; }
13.
public
decimal
Quantity {
get
;
set
; }
14.
}
並且修改我們原本在 Web API Controller 裡的 Function:
view sourceprint?
01.
public
IEnumerable<OrderDTO> GetOrders()
02.
{
03.
NorthwindEntities db =
new
NorthwindEntities();
04.
05.
return
db.Orders.ToList().Select(p =>
new
OrderDTO
06.
{
07.
CustomerID = p.CustomerID,
08.
EmployeeID = p.EmployeeID,
09.
OrderDate = p.OrderDate,
10.
OrderID = p.OrderID,
11.
Order_Detail = p.Order_Details.Select(x =>
new
Order_DetailsDTO
12.
{
13.
OrderID = x.OrderID,
14.
Quantity = x.Quantity,
15.
UnitPrice = x.UnitPrice
16.
}).ToList()
17.
}); ;
18.
}
總結
兩種作法都能解決造成迴圈對象參考的問題,而在 Bruce 和 Will 保哥的文章都指出用第一種方法來解決此迴圈參考錯誤是最正確的作法,兩種作法算是兩種不同的出發點,所以該怎麼用應該就是看各位讀者如何應用了。
解決ASP.NET Web API Json對象迴圈參考錯誤