c# 序列化'SubSonic.Schema.DatabaseColumn'类型的对象时检测到循环引用。



.net json (12)

我正在尝试做一个简单的JSON返回,但我遇到了以下问题。

public JsonResult GetEventData()
{
    var data = Event.Find(x => x.ID != 0);
    return Json(data);
}

我收到了一个HTTP 500,这个问题的标题中显示了这个例外。 我也试过了

var data = Event.All().ToList()

这给了同样的问题。

这是一个错误还是我的实现?


Answer #1

您可以注意到引起循环引用的属性。 然后你可以做一些事情:

private Object DeCircular(Object object)
{
   // Set properties that cause the circular reference to null

   return object
}

Answer #2

这实际上是因为复杂对象是导致json对象失败的原因。 并且它失败了,因为当对象被映射时,它映射映射他们父母的孩子,使循环引用发生。 Json需要无限的时间来序列化它,所以它可以防止出现异常的问题。

实体框架映射也产生相同的行为,解决方案是放弃所有不需要的属性。

只是说明最终答案,整个代码将是:

public JsonResult getJson()
{
    DataContext db = new DataContext ();

    return this.Json(
           new {
                Result = (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
               }
           , JsonRequestBehavior.AllowGet
           );
}

如果您不希望Result属性中包含对象,则也可以如下所示:

public JsonResult getJson()
{
    DataContext db = new DataContext ();

    return this.Json(
           (from obj in db.Things select new {Id = obj.Id, Name = obj.Name})
           , JsonRequestBehavior.AllowGet
           );
}

Answer #3

我正在使用修补程序,因为在MVC5视图中使用Knockout。

在行动

return Json(ModelHelper.GetJsonModel<Core_User>(viewModel));

功能

   public static TEntity GetJsonModel<TEntity>(TEntity Entity) where TEntity : class
    {
        TEntity Entity_ = Activator.CreateInstance(typeof(TEntity)) as TEntity;
        foreach (var item in Entity.GetType().GetProperties())
        {
            if (item.PropertyType.ToString().IndexOf("Generic.ICollection") == -1 && item.PropertyType.ToString().IndexOf("SaymenCore.DAL.") == -1)
                item.SetValue(Entity_, Entity.GetPropValue(item.Name));
        }
        return Entity_;  
    }

Answer #4

解决此问题的一个更简单的替代方法是返回一个字符串,并使用JavaScriptSerializer将该字符串格式化为json。

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
   return j.Serialize(entityList );
}

“选择”部分非常重要,该部分可以在视图中选择所需的属性。 某些对象有父母的参考。 如果您不选择属性,则可能会出现循环引用,如果您只是整个表格。

不要这样做:

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.toList();
   return j.Serialize(entityList );
}

如果您不想要整个表格,请这样做:

public string GetEntityInJson()
{
   JavaScriptSerializer j = new JavaScriptSerializer();
   var entityList = dataContext.Entitites.Select(x => new { ID = x.ID, AnotherAttribute = x.AnotherAttribute });
   return j.Serialize(entityList );
}

这有助于以更少的数据呈现视图,只需使用您需要的属性,并使网络运行更快。


Answer #5

看来你的对象层次结构中有循环引用,这是JSON序列化程序不支持的。 你需要所有的列吗? 你可以在视图中只拾取你需要的属性:

return Json(new 
{  
    PropertyINeed1 = data.PropertyINeed1,
    PropertyINeed2 = data.PropertyINeed2
});

这将使您的JSON对象更轻松,更易于理解。 如果您有很多属性,可以使用AutoMapper在DTO对象和View对象之间automatically映射。


Answer #6

总结一下,有三种解决方案:

    private DBEntities db = new DBEntities();//dbcontext

    //Solution 1: turn off ProxyCreation for the DBContext and restore it in the end 
    public ActionResult Index()
    {
        bool proxyCreation = db.Configuration.ProxyCreationEnabled;
        try
        {
            //set ProxyCreation to false
            db.Configuration.ProxyCreationEnabled = false;

            var data = db.Products.ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
        finally
        {
            //restore ProxyCreation to its original state
            db.Configuration.ProxyCreationEnabled = proxyCreation;
        }
    }

    //Solution 2: Using JsonConvert by Setting ReferenceLoopHandling to ignore on the serializer settings. 
    //using using Newtonsoft.Json;
    public ActionResult Index()
    {
        try
        {
            var data = db.Products.ToList();

            JsonSerializerSettings jss = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
            var result = JsonConvert.SerializeObject(data, Formatting.Indented, jss);

            return Json(result, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

    //Solution 3: return a new dynamic object which includes only the needed properties.
    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new
                                                {
                                                    Product_ID = p.Product_ID,
                                                    Product_Name = p.Product_Name,
                                                    Product_Price = p.Product_Price
                                                }).ToList();

            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            Response.StatusCode = (int)HttpStatusCode.BadRequest;
            return Json(ex.Message);
        }
    }

Answer #7

避免直接转换表格对象。 如果关系在其他表之间设置,它可能会抛出此错误。 相反,您可以创建模型类,将值分配给类对象,然后对其进行序列化。


Answer #8

提供的答案很好,但我认为可以通过添加“架构”角度来改进。

调查

MVC's Controller.Json函数正在完成这项工作,但在这种情况下提供相关错误的能力很差。 通过使用Newtonsoft.Json.JsonConvert.SerializeObject ,错误指定了触发循环引用的属性到底是什么。 这在序列化更复杂的对象层次结构时特别有用。

适当的架构

我们不应该尝试序列化数据模型(例如EF模型),因为ORM的导航属性是序列化过程中的灭亡之路。 数据流应该如下:

Database -> data models -> service models -> JSON string 

可以使用自动映射器(例如Automapper )从数据模型中获取服务模型。 尽管这并不能保证缺少循环引用,但正确的设计应该这样做:服务模型应该包含服务使用者需要的东西(即属性)。

在极少数情况下,当客户端请求在不同级别上涉及相同对象类型的层次结构时,该服务可以使用父 - 关关系(仅使用标识符而不是引用)创建线性结构。

现代应用程序倾向于避免一次加载复杂的数据结构,服务模型应该很小。 例如:

  1. 访问仅包含事件的头数据(标识符,名称,日期等) - >仅包含头数据的服务模型(JSON)
  2. 受管参与者列表 - 访问弹出窗口并延迟加载列表 - >仅包含与会者列表的服务模型(JSON)

Answer #9

我有同样的问题,并通过using Newtonsoft.Json;解决using Newtonsoft.Json;

var list = JsonConvert.SerializeObject(model,
    Formatting.None,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
});

return Content(list, "application/json");

Answer #10

[JsonIgnore]添加到虚拟模型中的属性。


Answer #11

使用Newtonsoft.Json:在您的Global.asax Application_Start方法中添加以下行:

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

Answer #12

JSON与xml和其他各种格式一样,是一种基于树的序列化格式。 如果你的对象中有循环引用,它就不会爱你,因为“树”是:

root B => child A => parent B => child A => parent B => ...

通常有沿某一路径禁用导航的方法; 例如,使用XmlSerializer您可以将父属性标记为XmlIgnore 。 我不知道这是可能的json序列化的问题,也不知道DatabaseColumn是否有合适的标记( 非常不可能,因为它需要引用每个序列化API)





subsonic