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


170

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

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

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

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

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

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


1
이것 좀 봐 ScriptIgnore속성을 사용하는 솔루션이 있습니다 . stackoverflow.com/questions/1193857/subsonic-3-0-0-2-structs-tt
freddoo

이것이 저에게 가장 좋은 해결책이었습니다. 나는 Game> Tournament> Game> Tournament> Game 등을 가졌다. 나는 ScriptIgnoreTournament.Game 속성에 속성을 배치했고 잘 작동했다 :)
eth0

: 경우 사람이 QA 체크 아웃, 더 추가 코드가 필요하지 않습니다이 문제에 대한 (하지 모범 사례) 솔루션 "자동"원한다 JSON (ServiceStack.Text 라이브러리)에서 엔티티 프레임 워크 클래스 참조를 직렬화하지 마십시오
nikib3ro

답변:


175

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

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

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


나는 내가 원하는 것을 선택하는 것이 효과가 있다고 생각한다. 순환 참조는 이벤트에서 IQueryable <Category>를 가지고 있기 때문에 IQueryable <Event>를 가질 것이기 때문이다.
Jon

7
Automapper는이 문제가 발생하지 않는다고 보장하지 않습니다. 나는 대답을 찾기 위해 여기에 왔고 실제로 automapper를 사용하고 있습니다.
선장 켄 파치

1
@ClayKaboom의 답변을 참조하십시오. 왜 그것이 순환 일 수 있는지 설명합니다.
PandaWood

106

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

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

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

3
이 인라인 코드는 저에게 효과적이었습니다. kravits88에서 언급 한 전역 구성의 동일한 내용이 작동하지 않습니다. 또한이 코드에 대한 ContentResult를 반환하도록 메서드 서명을 업데이트해야합니다.
BiLaL

6
허용 된 것으로 표시된 답변에서와 같이 객체를 다른 표현으로 변환하는 데 시간을 소비 할 수없는 경우를 다루므로 이는 최상의 답변으로 표시되어야합니다.
Renan

56

복잡한 객체가 결과 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
           );
}

1
@Clay 덕분에 명확하고 이해하기 쉬운 +1 오류 뒤에있는 개념에 대한 설명이 마음에 듭니다.
Ajay2707

14

요약하면 다음과 같은 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);
        }
    }

7

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

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

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


4

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

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

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

    context.Configuration.ProxyCreationEnabled = 거짓;

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

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


4

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

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

1
분명히 매우 직설적으로 보이지만 나에게는 효과가 없었습니다
BiLaL


4

테이블 개체를 직접 변환하지 마십시오. 다른 테이블간에 관계가 설정되면이 오류가 발생할 수 있습니다. 오히려 모델 클래스를 만들고 클래스 객체에 값을 할당 한 다음 직렬화 할 수 있습니다.


3

제공된 답변은 좋지만 "아키텍처"관점을 추가하여 개선 할 수 있다고 생각합니다.

조사

MVC's Controller.Json함수가 작업을 수행하고 있지만이 경우 관련 오류를 제공하는 것이 매우 열악합니다. 를 사용 Newtonsoft.Json.JsonConvert.SerializeObject하면 오류가 순환 참조를 트리거하는 특성을 정확히 지정합니다. 보다 복잡한 객체 계층을 직렬화 할 때 특히 유용합니다.

적절한 아키텍처

ORM의 내비게이션 속성은 직렬화와 관련하여 당혹의 길이 기 때문에 데이터 모델 (예 : EF 모델)을 직렬화하려고 시도해서는 안됩니다. 데이터 흐름은 다음과 같아야합니다.

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

서비스 모델은 자동 매퍼 (예 : Automapper )를 사용하여 데이터 모델에서 얻을 수 있습니다 . 이것이 순환 참조의 부족을 보장하지는 않지만 적절한 설계는이를 수행해야합니다. 서비스 모델에는 서비스 소비자가 요구하는 것 (예 : 속성)이 정확하게 포함되어야합니다.

드문 경우이지만 클라이언트가 서로 다른 수준에서 동일한 객체 유형을 포함하는 계층을 요청하면 서비스는 부모-> 자식 관계 (참조가 아닌 식별자 만 사용)를 갖는 선형 구조를 만들 수 있습니다.

최신 응용 프로그램은 복잡한 데이터 구조를 한 번에로드하지 않는 경향이 있으며 서비스 모델은 적어야합니다. 예 :

  1. 이벤트에 액세스-헤더 데이터 (식별자, 이름, 날짜 등) 만로드 됨-> 헤더 데이터 만 포함하는 서비스 모델 (JSON)
  2. 관리 대상 참석자 목록-팝업에 액세스하고 지연 대상자 목록-> 참석자 목록 만 포함 된 서비스 모델 (JSON)을로드합니다.

1

MVC5보기에서 녹아웃을 사용하기 때문에 수정 프로그램을 사용하고 있습니다.

행동에

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

0

순환 참조를 유발하는 특성을 확인할 수 있습니다. 그런 다음 다음과 같은 작업을 수행 할 수 있습니다.

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

   return object
}

-1
//first: Create a class as your view model

public class EventViewModel 
{
 public int Id{get;set}
 public string Property1{get;set;}
 public string Property2{get;set;}
}
//then from your method
[HttpGet]
public async Task<ActionResult> GetEvent()
{
 var events = await db.Event.Find(x => x.ID != 0);
 List<EventViewModel> model = events.Select(event => new EventViewModel(){
 Id = event.Id,
 Property1 = event.Property1,
 Property1 = event.Property2
}).ToList();
 return Json(new{ data = model }, JsonRequestBehavior.AllowGet);
}

이것은 질문에 대한 답이 아닙니다
Dane I

-1

이 문제를 해결하는 더 쉬운 대안은 문자열을 반환하고 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 );
}

이렇게하면 필요한 속성만으로 적은 데이터로보기를 렌더링하고 웹을 더 빠르게 실행할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.