IT

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

lottoking 2020. 5. 28. 08:22
반응형

'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를 참조해야하기 때문에 매우 가능성이 낮습니다)


Its because of the new DbContext T4 template that is used for generating the EntityFramework entities. In order to be able to perform the change tracking, this templates uses the Proxy pattern, by wrapping your nice POCOs with them. This then causes the issues when serializing with the JavaScriptSerializer.

So then the 2 solutions are:

  1. Either you just serialize and return the properties you need on the client
  2. You may switch off the automatic generation of proxies by setting it on the context's configuration

    context.Configuration.ProxyCreationEnabled = false;

Very well explained in the below article.

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


Using Newtonsoft.Json: In your Global.asax Application_Start method add this line:

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

add [JsonIgnore] to virtuals properties in your model.


Avoid converting the table object directly. If relations are set between other tables, it might throw this error. Rather, you can create a model class, assign values to the class object and then serialize it.


Provided answers are good, but I think they can be improved by adding an "architectural" perspective.

Investigation

MVC's Controller.Json function is doing the job, but it is very poor at providing a relevant error in this case. By using Newtonsoft.Json.JsonConvert.SerializeObject, the error specifies exactly what is the property that is triggering the circular reference. This is particularly useful when serializing more complex object hierarchies.

Proper architecture

One should never try to serialize data models (e.g. EF models), as ORM's navigation properties is the road to perdition when it comes to serialization. Data flow should be the following:

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

Service models can be obtained from data models using auto mappers (e.g. Automapper). While this does not guarantee lack of circular references, proper design should do it: service models should contain exactly what the service consumer requires (i.e. the properties).

In those rare cases, when the client requests a hierarchy involving the same object type on different levels, the service can create a linear structure with parent->child relationship (using just identifiers, not references).

Modern applications tend to avoid loading complex data structures at once and service models should be slim. E.g.:

  1. access an event - only header data (identifier, name, date etc.) is loaded -> service model (JSON) containing only header data
  2. managed attendees list - access a popup and lazy load the list -> service model (JSON) containing only the list of attendees

I'm Using the fix, Because Using Knockout in MVC5 views.

On action

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

function

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

You can notice the properties that cause the circular reference. Then you can do something like:

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

An easier alternative to solve this problem is to return an string, and format that string to json with JavaScriptSerializer.

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

It is important the "Select" part, which choose the properties you want in your view. Some object have a reference for the parent. If you do not choose the attributes, the circular reference may appear, if you just take the tables as a whole.

Do not do this:

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

Do this instead if you don't want the whole table:

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

This helps render a view with less data, just with the attributes you need, and makes your web run faster.

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

반응형