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


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


前言

一般我们在开法 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 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

OrdersMetadata.cs


    [MetadataType(typeof(OrderMD))]
    public partial class Order
    {
        public class OrderMD
        {
            [JsonIgnore()] // 需引用 using Newtonsoft.Json;
            public virtual ICollection Order_Details { get; set; }
        }
    }

Order_DetailsMetadata.cs


    [MetadataType(typeof(Order_DetailsMD))]
    public partial class Order_Details
    {
        public class Order_DetailsMD
        {
            [JsonIgnore()]  // 需引用 using Newtonsoft.Json;
            public virtual Orders Orders { get; set; }
        }
    }

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

方法二:

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

DTO.cs


    public class OrderDTO
    {
        public int OrderID { get; set; }
        public string CustomerID { get; set; }
        public int? EmployeeID { get; set; }
        public DateTime? OrderDate { get; set; }
        public List Order_Detail { get; set; }
    }

    public class Order_DetailsDTO
    {
        public int OrderID { get; set; }
        public decimal UnitPrice { get; set; }
        public decimal Quantity { get; set; }
    }

并且修改我们原本在 Web API Controller 里的 Function:


        public IEnumerable GetOrders()
        {
            NorthwindEntities db = new NorthwindEntities();
           
            return db.Orders.ToList().Select(p => new OrderDTO
            {
                CustomerID = p.CustomerID,
                EmployeeID = p.EmployeeID,
                OrderDate = p.OrderDate,
                OrderID = p.OrderID,
                Order_Detail = p.Order_Details.Select(x => new Order_DetailsDTO
                {
                    OrderID = x.OrderID,
                    Quantity = x.Quantity,
                    UnitPrice = x.UnitPrice
                }).ToList()
            }); ;
        }

总结

两种作法都能解决造成循环对象参考的问题,而在 Bruce 和 Will 保哥的文章都指出用第一种方法来解决此循环参考错误是最正确的作法,两种作法算是两种不同的出发点,所以该怎么用应该就是看各位读者如何应用了。


参考来源

  • ASP.NET WEB API - ENTITY FRAMEWORK(EDMX) NAVIGATION PROPERTY引发的JSON对象循环参考错误
  • ASP.NET Web API 无法输出 Entity Framework 对象的解法

新手发文,如有错误烦请告知,感谢。
如果喜欢我的文章请按推荐,有任何问题欢迎下面留言~~~


签名:

学习这条路很广,喜欢什么技术不重要,重要的是你肯花时间去学习