您的位置:首页 > 编程语言 > ASP

解决ASP.NET Web API Json对象循环参考错误

2015-02-11 09:35 344 查看
前言

一般我们在开法ASP.NETWebAPI时,如果是使用EntityFramework技术来操作数据库的话,当两个Entity之间包含导览属性(NavigationProperty)时,而当我们输出的格式为JSON对象时,会出现一个例外,错误讯息为:「'ObjectContent`1'类型无法序列化内容类型'application/json;charset=utf-8'的回应主体。」,而小弟参考了Will保哥以及Bruce两位前辈的文章后,整理出两种小弟觉得比较可行的替代与解决方案。

了解问题



这张图里包含了两张数据表Orders与Order_Details,两者之间存在着一对多的关系,而预设EntityFramework会自动帮我们的关联数据表加入导览属性(NavigationProperty),接着我们往下一张图看下去:

publicIEnumerable<Orders>GetOrders(){returndb.Orders;}

这段程序代码为ValuesController里的一个Function,当我们请求时会返回Orders所有数据,但当我们输入网址/api/Values/请求时却发生了这样的错误:



这个问题发生的原因为,当我们请求某个特定的Enity时会取出该Entity的所有属性内容,当然包夸了导览属性的数据,而究竟这个问题如何照成呢?以目前的案例来看,当我们取得Orders的资料时也会一并取得其导览属性,也就是Order_Details的所有数据,而在Order_Details里也包含着Orders的导览属性,所以又会在去取得Orders的数据....,这样两个实体之间不停的往返就会造成了无限循环,也是我们前面所说的循环参考的错误。

如何解决

方法一:

在开发ASP.NETMVC中,时常会用到部分类别(PartailClass)来为我们的数据域位加上验证属性,所以利用此特性来解决我们的问题,首先在Model数据夹底下新增两个档案分别为:OrdersMetadata.cs、Order_DetailsMetadata.cs

viewsourceprint?

01.
OrdersMetadata.cs

02.

03.
[MetadataType(
typeof
(OrderMD))]

04.
public
partial
class
Order

05.
{

06.
public
class
OrderMD

07.
{

08.
[JsonIgnore()]
//需引用usingNewtonsoft.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()]
//需引用usingNewtonsoft.Json;

19.
public
virtual
OrdersOrders{
get
;
set
;}

20.
}

21.
}


这边我们在在对应的导览属性上都加上「JsonIgnore」的属性,来防止循环参考的问题发生,记得是有关联的两边都要加上「JsonIgnore」的属性。

方法二:

另外一种方法则是利用我们在开发ASP.NETMVC时常用到的ViewModel的概念,针对特定的页面或请求只返回特定的数据,所以这边我们能透过DTO方式来解决我们问题,首先我们先在Model底下新增一个DTO.cs档案:www.it165.net

DTO.cs

viewsourceprint?

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.
}


并且修改我们原本在WebAPIController里的Function:

viewsourceprint?

01.
public
IEnumerable<OrderDTO>GetOrders()

02.
{

03.
NorthwindEntitiesdb=
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保哥的文章都指出用第一种方法来解决此循环参考错误是最正确的作法,两种作法算是两种不同的出发点,所以该怎么用应该就是看各位读者如何应用了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: