sourcecode

'SubSonic' 유형의 개체를 직렬화하는 동안 순환 참조가 탐지되었습니다.스키마Database Column'을 클릭합니다.

copyscript 2023. 2. 7. 20:04
반응형

'SubSonic' 유형의 개체를 직렬화하는 동안 순환 참조가 탐지되었습니다.스키마Database Column'을 클릭합니다.

간단한 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은 이를 직렬화하는 데 시간이 무한히 걸리기 때문에 예외로 인한 문제를 방지할 수 있습니다.

엔티티 프레임워크 매핑에서도 동일한 동작이 발생합니다.해결책은 불필요한 속성을 모두 폐기하는 것입니다.

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

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

,, 이, 이, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, 음, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,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: JsonConvert를 사용하여 시리얼라이저 설정에서 무시하도록 ReferenceLoopHandling을 설정합니다.

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

JSON은 xml 및 기타 다양한 형식과 마찬가지로 트리 기반의 시리얼라이제이션 형식입니다.오브젝트에 "트리"와 같이 순환 참조가 있는 경우 다음과 같이 표시되지 않습니다.

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

특정.XmlSerializer을 '모재산으로 '라고 할 수 .XmlIgnore가 되고 serializer에서 도 알 수 없고, json serializer에서도 한지 여부도 알 수 DatabaseColumn(모든 시리얼화 API를 참조할 필요가 있기 때문에 가능성이 매우 낮다)

[JsonIgnore]를 사용하여 모델의 속성을 가상화할 수 있습니다.

뉴턴소프트 사용.Global.asax Application_Start 메서드에서 다음 행을 추가합니다.

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

이는 EntityFramework엔티티를 생성하기 위해 사용되는 새로운 DbContext T4 템플릿 때문입니다.이 템플릿은 변경 추적을 수행하기 위해 Proxy 패턴을 사용합니다.이러한 패턴은 적절한 POCO로 포장되어 있습니다.그러면 JavaScriptSerializer를 사용하여 시리얼화할 때 문제가 발생합니다.

다음 2가지 솔루션이 있습니다.

  1. 클라이언트에 필요한 속성을 시리얼화하여 반환하거나 둘 중 하나
  2. 컨텍스트 구성에 프록시 자동 생성을 설정하여 프록시 자동 생성을 끌 수 있습니다.

    맥락.배열.ProxyCreationEnabled = false;

아래 기사에 잘 설명되어 있습니다.

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

제공된 답변도 좋지만, "건축적" 관점을 추가함으로써 개선될 수 있다고 생각합니다.

조사

MVC's Controller.Json함수는 작업을 수행하지만 이 경우 관련 오류를 제공하는 데 매우 부족합니다.「」를 사용해 .Newtonsoft.Json.JsonConvert.SerializeObject에러는 순환 참조를 트리거하는 속성을 정확하게 지정합니다.이것은 특히 더 복잡한 객체 계층을 직렬화할 때 유용합니다.

적절한 아키텍처

데이터 모델(예: EF 모델)을 직렬화하려고 하면 안 됩니다. ORM의 내비게이션 속성은 직렬화에 있어 파멸의 길목이기 때문입니다.데이터 흐름은 다음과 같습니다.

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

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

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

최신 애플리케이션은 복잡한 데이터 구조를 한 번에 로드하는 것을 피하는 경향이 있으며, 서비스 모델은 슬림해야 합니다.예:

  1. 이벤트 액세스 - 헤더 데이터(식별자, 이름, 날짜 등)만 로드 -> 헤더 데이터만 포함하는 서비스 모델(JSON)
  2. managed attendants list - 팝업 접근 -> 참가자 목록만 포함하는 목록 로드 -> 서비스 모델(JSON)

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

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

순환 참조의 원인이 되는 속성을 확인할 수 있습니다.그런 다음 다음과 같은 작업을 수행할 수 있습니다.

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

   return object
}
//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);
}

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

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

언급URL : https://stackoverflow.com/questions/1153385/a-circular-reference-was-detected-while-serializing-an-object-of-type-subsonic

반응형