根据我们不同的需要,我们会在不同的时候选择序列化和反序列化,从而得到我们想要的对象或者字符串。
今天在用MVC+EF做一个简单查询的时候,当返回json格式的数据时出现问题了。
<span style="font-family:KaiTi_GB2312;font-size:18px;"> public ActionResult QueryBasicInfo() { //获得B层 IBasicInfoBll test = SpringHelper.GetObject<IBasicInfoBll>("BasicInfoBll"); //获得页码索引值 int pageIndex = Request["page"] == null ? 1 : int.Parse(Request["page"]); //获得页容量 int pageSize = Request["rows"] == null ? 10 : int.Parse(Request["rows"]); int total = 0; //调用B层的方法,进行分页查询 List<T_BasicInformation> BasicRes = test.QueryBasicInfo(pageSize, pageIndex, out total); var data = BasicRes.Select( p=>new { total, Rows=BasicRes }); return Json(data, JsonRequestBehavior.AllowGet); }</span>很简单的一个查询,能返回数据,但是就在最后一句序列化报错:
于是上网搜索解决办法,网上提供了很多办法,主要有以下几种:
1.最简单的方式就是从Entity Framework着手,停用LazyLoading与ProxyCreation.因为LazyLoading停用后,那么当JSON.Net解析Order对象时其属性Order_Details会返回null(不会自动加载).所以也就避免了此问题。
当然此方式的缺点会导致后续程序存取Entity Object时牺牲了LazyLoading的方便性,需要手动处理此问题.
<span style="font-family:KaiTi_GB2312;font-size:18px;"> db.Configuration.LazyLoadingEnabled = false;
db.Configuration.ProxyCreationEnabled = false;
return db.Orders.AsEnumerable();</span>
2.设定JSON.Net忽略循环参考
,透过APP_Start的WebApiConfig.cs设定
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
使用这个方式时要注意,它只是忽略循环参考的错误,但实际上还是会自动一层层解析要输出的对象之属性,所以若数据会相依有可能会产生无穷循环.
3.设定JSON.Net避免循环参考,透过APP_Start的WebApiConfig.cs,设定
<span style="font-size:18px;">config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize; config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;</span>这种做法与2的差异在于它会将重复过的对象用一个代表取代,譬如底下JSON格式:
[{"$id":"1","Category":{"$id":"2","Products":[{"$id":"3","Category":{"$ref":"2"},"Id":2,"Name":"Yogurt"},{"$ref":"1"}],"Id":1,"Name":"Diary"},"Id":1,"Name":"Whole Milk"},{"$ref":"3"}]
所以对于数据而言这种作法还是会自动一层层解析要输出的对象之属性,只是避免输出太大量数据.
4.手动设定避免循环参考
如同3的模式,透过[JsonIgnore] 与[JsonObject(IsReference = true)] 细部设定,可以更精确的设定每个要输出的属性.
缺点是1.设定繁杂. 2.只能通用设定无法例外. 3.因为必须直接或透过 partial class方式设定,故无法将设定与Entity Object class做分离。
查了很多资料之后,自己好像懂了,原因就是,因为这个表和另一个表是有一对多关系的,当序列化表1的时候,会找到和另一个表2关联的字段,就会到另一个表2中序列化,然后另一个表2中也有一个字段和表1相关联.这样.序列化就会发生这种错误!
最后将上面的LINQ语句改成了下面语句:
<span style="font-family:KaiTi_GB2312;font-size:18px;"> var data = BasicRes.Select( p=>new { p.name, p.sex, p.idNumber, p.receivemode, p.myidentity, p.position, p.oldworkplace, p.nowworkplace, p.isrecord, total, Rows=BasicRes.Count });</span>为什么能够解决呢,我想是因为这样做,我就只拿我需要的字段,不需要的字段,我就不拿。网上的解决办法不一定适用我们的问题,但是可以帮我们打开一些思路,所以我们要更加积极地区思考问题。