[C#] ‘SubSonic.Schema .DatabaseColumn’유형의 오브젝트를 직렬화하는 중에 순환 참조가 발견되었습니다.

간단한 JSON 반환을 시도하고 있지만 아래에 다음과 같은 문제가 있습니다.

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

이 질문의 제목에 표시된 예외를 제외하고 HTTP 500을 얻습니다. 나는 또한 시도했다

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

같은 문제가 발생했습니다.

이것이 버그입니까, 아니면 제 구현입니까?



답변

JSON 시리얼 라이저가 지원하지 않는 객체 계층 구조에 순환 참조가있는 것 같습니다. 모든 열이 필요합니까? 뷰에서 필요한 속성 만 선택할 수 있습니다.

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

이것은 JSON 객체를 더 가볍고 이해하기 쉽게 만듭니다. 많은 속성이있는 경우 AutoMapper를 사용하여 DTO 객체와 View 객체 를 자동으로 매핑 할 수 있습니다 .


답변

나는 같은 문제를 겪었고 using Newtonsoft.Json;

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

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


답변

복잡한 객체가 결과 json 객체를 실패하게하기 때문에 실제로 발생합니다. 그리고 객체가 매핑되면 자식을 매핑하고 부모를 매핑하여 순환 참조가 발생하기 때문에 실패합니다. Json은 직렬화하는 데 시간이 오래 걸리므로 예외로 인한 문제를 방지합니다.

Entity Framework 매핑도 동일한 동작을 생성하며 솔루션은 원치 않는 모든 속성을 삭제하는 것입니다.

최종 답변을 명시하면 전체 코드는 다음과 같습니다.

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
           );
}


답변

요약하면 다음과 같은 4 가지 솔루션이 있습니다.

해결 방법 1 : DBContext에 대한 ProxyCreation을 끄고 결국 복원하십시오.

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

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

해결 방법 2 : Serializer 설정에서 무시하도록 ReferenceLoopHandling을 설정하여 JsonConvert 사용

    //using using Newtonsoft.Json;

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

    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);
        }
    }

다음 두 가지 솔루션은 동일하지만 강력한 형식이므로 모델을 사용하는 것이 좋습니다.

해결 방법 3 : 필요한 속성 만 포함 된 모델을 반환하십시오.

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

    public class ProductModel
    {
        public int Product_ID { get; set;}

        public string Product_Name { get; set;}

        public double Product_Price { get; set;}
    }

    public ActionResult Index()
    {
        try
        {
            var data = db.Products.Select(p => new ProductModel
                                                {
                                                    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);
        }
    }

해결 방법 4 : 필요한 속성 만 포함하는 새로운 동적 객체를 반환합니다.

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

    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);
        }
    }


답변

xml 및 기타 다양한 형식과 마찬가지로 JSON은 트리 기반 직렬화 형식입니다. “트리”는 다음과 같이 객체에 순환 참조가 있으면 사랑하지 않습니다.

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

특정 경로를 따라 탐색을 비활성화하는 방법이 종종 있습니다. 예를 들어 XmlSerializer부모 속성을로 표시 할 수 있습니다 XmlIgnore. 해당 json serializer 로 이것이 가능한지 또는 DatabaseColumn적절한 마커가 있는지 여부 는 알 수 없습니다 ( 매우 직렬화 API를 참조해야하기 때문에 매우 가능성이 낮습니다)


답변

EntityFramework 엔터티를 생성하는 데 사용되는 새로운 DbContext T4 템플릿 때문입니다. 변경 추적을 수행 할 수 있도록이 템플릿은 멋진 POCO를 감싸서 프록시 패턴을 사용합니다. 그러면 JavaScriptSerializer로 직렬화 할 때 문제가 발생합니다.

따라서 두 가지 해결책은 다음과 같습니다.

  1. 클라이언트에서 필요한 속성을 직렬화하고 반환하면됩니다.
  2. 컨텍스트의 구성에서 프록시 자동 생성을 설정하여 자동 생성을 끌 수 있습니다

    context.Configuration.ProxyCreationEnabled = 거짓;

아래 기사에서 잘 설명했습니다.

http://juristr.com/blog/2011/08/javascriptserializer-circular-reference/


답변

Newtonsoft.Json 사용 : Global.asax Application_Start 메소드에서 다음 행을 추가하십시오.

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