자체 참조 루프 감지 됨-WebApi에서 브라우저로 데이터 가져 오기


80

Entity Framework를 사용하고 있으며 브라우저에 부모 및 자식 데이터를 가져 오는 데 문제가 있습니다. 내 수업은 다음과 같습니다.

 public class Question
 {
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public virtual ICollection<Answer> Answers { get; set; }
}

public class Answer
{
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public virtual Question Question { get; set; }
}

다음 코드를 사용하여 질문 및 답변 데이터를 반환하고 있습니다.

    public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
    {
        var questions = _questionsRepository.GetAll()
            .Where(a => a.SubTopicId == subTopicId &&
                   (questionStatusId == 99 ||
                    a.QuestionStatusId == questionStatusId))
            .Include(a => a.Answers)
            .ToList();
        return questions; 
    }

C # 측면에서 이것은 작동하는 것처럼 보이지만 대답 개체에 질문에 대한 참조가 있음을 알 수 있습니다. WebAPI를 사용하여 데이터를 브라우저로 가져올 때 다음 메시지가 표시됩니다.

'ObjectContent`1'유형이 콘텐츠 유형 'application / json;에 대한 응답 본문을 직렬화하지 못했습니다. charset = utf-8 '입니다.

유형이 'Models.Core.Question'인 속성 'question'에 대해 자체 참조 루프가 감지되었습니다.

질문에 답변이 있고 답변에 질문에 대한 참조가 있기 때문입니까? 내가 본 모든 장소는 아이의 부모에 대한 언급을 제안하므로 어떻게 해야할지 모르겠습니다. 누군가 나에게 이것에 대한 조언을 줄 수 있습니까?


6
웹 API의 사용 DTO, 직접 reaponse에 반환 법인을 피하고
cuongle

Dto는 무엇입니까? 우리의 전체 응용 프로그램은 EF를 사용하고 클라이언트에서 AngularJS를 사용하고 있으며이 경우 외에는 문제가 없습니다.

1
내가 의미하는 바는 웹 API에 대해 Dto를 정의해야한다는 의미입니다. Dto는 MVC의 ViewModel과 유사합니다. Dto는 클라이언트 (angularjs)에 데이터를 제공하는 EF 모델의 래퍼와 같습니다.
cuongle


JSON.Net 페이지 에서 "Self Referencing Loop Detected"예외 에 대한 내 대답을 볼 수 있습니다 .
Murat Yıldız

답변:


73

질문에 답변이 있고 답변에 질문에 대한 참조가 있기 때문입니까?

예. 직렬화 할 수 없습니다.

편집 : Tallmaris의 답변과 OttO의 의견을 참조하십시오. 더 간단하고 전역 적으로 설정할 수 있습니다.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌​ferenceLoopHandling = ReferenceLoopHandling.Ignore;

이전 답변 :

EF 개체 Question를 자신의 중간 또는 DataTransferObject에 투영합니다 . 그러면이 Dto를 성공적으로 직렬화 할 수 있습니다.

public class QuestionDto
{
    public QuestionDto()
    {
        this.Answers = new List<Answer>();
    } 
    public int QuestionId { get; set; }
    ...
    ...
    public string Title { get; set; }
    public List<Answer> Answers { get; set; }
}

다음과 같은 것 :

public IList<QuestionDto> GetQuestions(int subTopicId, int questionStatusId)
{
    var questions = _questionsRepository.GetAll()
        .Where(a => a.SubTopicId == subTopicId &&
               (questionStatusId == 99 ||
                a.QuestionStatusId == questionStatusId))
        .Include(a => a.Answers)
        .ToList();

    var dto = questions.Select(x => new QuestionDto { Title = x.Title ... } );

    return dto; 
}

3
추가하고 싶습니다 .ReferenceLoopHandling.Ignore 설정이 작동하지 않았거나 전역 설정 또는 API 시작이 전혀 작동하지 않았습니다. 자식 클래스의 탐색 속성을 [JsonIgnore]로 장식하여 작동하도록 관리했습니다. 여전히 ParentId를 얻지 만 직렬화하는 동안 Parent 탐색이 무시됩니다.
Claiton Lovato 2015

안녕하세요, 직렬화를 무시하면 순환 종속성 Question> Answer> Question이 깨집니다. DTO 접근 방식이이를 보존합니까?
Bartosz

이전 ASP.NET MVC 프로젝트에서이 문제가 있습니다. GlobalConfiguration.Configuration에 포맷터가 없습니다. 당신은 그것을 위해 무엇을 할 수 있는지 제안 할 수 있습니까?
Ren

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Re‌ ferenceLoopHandling = ReferenceLoopHandling.Ignore; ->이 코드 줄을 어디에 넣을까요 ???
anhtv13

56

다음에서 시도해 볼 수도 있습니다 Application_Start().

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;

많은 후프를 거치지 않고 문제를 해결해야합니다.


편집 : 아래 OttO의 의견에 따라 ReferenceLoopHandling.Ignore대신 사용하십시오 .

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

78
나는 이것이 오래된 스레드라는 것을 알고 있지만 나중에 그것을 우연히 발견하는 사람들을 위해 다음을 시도하십시오. GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
OttO 2014 년

@OttO, 귀하의 제안이 저에게 효과적이었습니다. 매우 감사합니다.
J86 2014-07-12

2
코드가 무한 루프에 들어가고이 줄을 추가 한 후 스택 오버플로 예외가 표시됩니다.
Microsoft Developer

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; 잘 작동
드라고 Durlut

@Demodave Create()는 매개 변수로 설정을 허용하는 정적 메서드를 사용하여 JsonSerializer를 만들어야합니다 . 문서 도구 : newtonsoft.com/json/help/html/...
Tallmaris

21

OWIN을 사용하는 경우 더 이상 GlobalSettings가 필요하지 않습니다! IAppBuilder UseWebApi 함수 (또는 사용중인 서비스 플랫폼)에 전달되는 HttpConfiguration 개체에서 이와 동일한 설정을 수정해야합니다.

이렇게 보일 것입니다.

    public void Configuration(IAppBuilder app)
    {      
       //auth config, service registration, etc      
       var config = new HttpConfiguration();
       config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
       //other config settings, dependency injection/resolver settings, etc
       app.UseWebApi(config);
}

1
당신은 내 하루를 구했습니다. 위의 답변이 작동하지 않는 이유가 궁금합니다. 예, Global.asax에서 OWIN 설정을 사용하면 작동하지 않습니다.
Sithu

21

ASP.NET Core에서 수정 사항은 다음과 같습니다.

services
.AddMvc()
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

5

DNX / MVC 6 / ASP.NET vNext를 사용하는 경우 어쩌구 저쩌구, 심지어 HttpConfiguration누락되었습니다. Startup.cs파일 에서 다음 코드를 사용하여 포맷터를 구성해야 합니다.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().Configure<MvcOptions>(option => 
        {
            //Clear all existing output formatters
            option.OutputFormatters.Clear();
            var jsonOutputFormatter = new JsonOutputFormatter();
            //Set ReferenceLoopHandling
            jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            //Insert above jsonOutputFormatter as the first formatter, you can insert other formatters.
            option.OutputFormatters.Insert(0, jsonOutputFormatter);
        });
    }

1
asp-net rc-1-final에서는 지금 "services.Configure <MvcOptions>"라고 생각합니다
Michał W.

JsonOutputFormatter는 네임 스페이스 Microsoft.AspNet.Mvc.Formatters
Sam

2
.NET Core 1.0 RTM의 경우 : new JsonOutputFormatter (serializerSettings, ArrayPool <char> .Shared);
aherrick

5

ASP.NET Core 웹 API (.NET Core 2.0) :

// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<MvcJsonOptions>(config =>
    {
        config.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    });
}

2

이것을 사용하여 :

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore

나를 위해 일하지 않았다. 대신 테스트를 위해 모델 클래스의 새롭고 단순화 된 버전을 만들었는데 제대로 돌아 왔습니다. 이 기사는 EF에서 훌륭하게 작동했지만 직렬화 할 수 없었던 내 모델에서 겪었던 몇 가지 문제에 대해 설명합니다.

http://www.asp.net/web-api/overview/data/using-web-api-with-entity-framework/part-4


1

ReferenceLoopHandling.Ignore가 작동하지 않았습니다. 내가 그것을 해결할 수있는 유일한 방법은 내가 원하지 않는 부모에 대한 링크를 코드를 통해 제거하고 내가 한 것을 유지하는 것입니다.

parent.Child.Parent = null;

1

.Net Framework 4.5를 사용하는 새로운 Asp.Net 웹 애플리케이션의 경우 :

Web Api : App_Start로 이동-> WebApiConfig.cs :

다음과 같이 보일 것입니다.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        // Configure Web API to use only bearer token authentication.
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

        // ReferenceLoopHandling.Ignore will solve the Self referencing loop detected error
        config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

        //Will serve json as default instead of XML
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

1

ASP.NET Core 3.0의 일부로 팀은 기본적으로 Json.NET을 포함하지 않았습니다. 이에 대한 자세한 내용은 [Including Json.Net to netcore 3.x] [1] https://github.com/aspnet/Announcements/issues/325에서 확인할 수 있습니다.

lazyloading을 사용하여 오류가 발생할 수 있습니다. services.AddDbContext (options => options.UseLazyLoadingProxies () ... 또는 db.Configuration.LazyLoadingEnabled = true;

수정 : startup.cs에 추가

 services.AddControllers().AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });

0

지연로드로 인해이 오류가 발생합니다. 따라서 내 제안은 속성에서 가상 키를 제거하는 것입니다. API로 작업하는 경우 지연로드는 API 상태에 좋지 않습니다.

구성 파일에 추가 줄을 추가 할 필요가 없습니다.

public class Question
 {
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public ICollection<Answer> Answers { get; set; }
}

public class Answer
{
    public int AnswerId { get; set; }
    public string Text { get; set; }
    public int QuestionId { get; set; }
    public Question Question { get; set; }
}

0

이 오류는 기존 데이터베이스의 edmx (개념적 모델을 정의하는 XML 파일)를 생성하고 부모 및 자식 테이블 모두에 대한 탐색 속성이있을 때 발생하는 것을 발견했습니다. 하위 항목 만 탐색하고 싶었 기 때문에 상위 개체에 대한 모든 탐색 링크를 삭제했고 문제가 해결되었습니다.


0

엔티티 db = new Entities ()

db.Configuration.ProxyCreationEnabled = false;

db.Configuration.LazyLoadingEnabled = false;


0

이 문제를 쉽게 해결하기 위해 새 자식 컬렉션을 동적으로 만들 수 있습니다.

public IList<Question> GetQuestions(int subTopicId, int questionStatusId)
    {
        var questions = _questionsRepository.GetAll()
            .Where(a => a.SubTopicId == subTopicId &&
                   (questionStatusId == 99 ||
                    a.QuestionStatusId == questionStatusId))
            .Include(a => a.Answers).Select(b=> new { 
               b.QuestionId,
               b.Title
               Answers = b.Answers.Select(c=> new {
                   c.AnswerId,
                   c.Text,
                   c.QuestionId }))
            .ToList();
        return questions; 
    }

0

위 답변의 구성 중 어느 것도 ASP.NET Core 2.2에서 저에게 효과적이지 않았습니다.

JsonIgnore내 가상 탐색 속성에 속성을 추가했습니다 .

public class Question
{
    public int QuestionId { get; set; }
    public string Title { get; set; }
    [JsonIgnore]
    public virtual ICollection<Answer> Answers { get; set; }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.