Foi detectado o possível ciclo de objetos do .Net Core 3.0 que não é suportado


22

Eu tenho 2 entidades que estão relacionadas como uma para muitas

public class Restaurant {
   public int RestaurantId {get;set;}
   public string Name {get;set;}
   public List<Reservation> Reservations {get;set;}
   ...
}
public class Reservation{
   public int ReservationId {get;set;}
   public int RestaurantId {get;set;}
   public Restaurant Restaurant {get;set;}
}

Se eu tentar obter restaurantes com reservas usando minha API

   var restaurants =  await _dbContext.Restaurants
                .AsNoTracking()
                .AsQueryable()
                .Include(m => m.Reservations).ToListAsync();
    .....

Eu recebo erro na resposta, porque os objetos contêm referências um ao outro. Existem posts relacionados que recomendam a criação de um modelo separado ou a adição da configuração do NewtonsoftJson

O problema é que eu não quero criar um modelo separado e a segunda sugestão não ajudou. Existe alguma maneira de carregar dados sem relação ciclada? *

System.Text.Json.JsonException: um possível ciclo de objeto foi detectado e não é suportado. Isso pode ser devido a um ciclo ou se a profundidade do objeto for maior que a profundidade máxima permitida de 32. no System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected (Int32 maxDepth) no System.Text.Json.JsonSerializer.Write (Utf8JsonWriter writer , Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions opções, WriteStack & state) em System.Text.Json.JsonSerializer.WriteAsyncCore (Stream utf8Json, Valor do objeto, Tipo inputType, JsonSerializerOptions opções, CancellationToken canceltingTextServiceFormattersTextSystem.Text. WriteResponseBodyAsync (contexto OutputFormatterWriteContext, Encoding selectedEncoding) em Microsoft.AspNetCore.Mvc.

*


Peça para ignorar a propriedade Restaurant da classe Reservation.
— Lasse V. Karlsen

6
Realmente, você não deve retornar suas entidades de banco de dados diretamente da sua API. Eu sugiro criar DTOs específicos da API e mapear adequadamente. Concedido que você disse que não queria fazer isso, mas consideraria uma boa prática geral manter a API e as persistências internas separadas.
— Mackie

"e a segunda sugestão não ajudou" precisa de detalhes.
— Henk Holterman

"O problema é que eu não quero criar um modelo separado". Seu design é fundamentalmente falho, a menos que você faça exatamente isso. Uma API é um contrato como uma interface (é literalmente uma interface de programação de aplicativos ). Ele nunca deve mudar, uma vez publicado, e qualquer alteração requer uma nova versão, que precisará ser executada simultaneamente com a versão antiga (que será descontinuada e eventualmente removida no futuro). Isso permite que os clientes atualizem suas implementações. Se você retornar uma entidade diretamente, estará acoplando firmemente sua camada de dados.
— Chris Pratt

Qualquer alteração nessa camada de dados exige uma alteração imediata e irreversível da API, interrompendo todos os clientes imediatamente até que eles atualizem suas implementações. Caso não seja óbvio, isso é uma coisa ruim. Em resumo: nunca aceite ou retorne entidades de uma API. Você sempre deve usar DTOs.
— Chris Pratt

Respostas:


32

Eu tentei o seu código em um novo projeto e a segunda maneira parece funcionar bem após a instalação do pacote Microsoft.AspNetCore.Mvc.NewtonsoftJson, primeiramente para o 3.0

services.AddControllerWithViews()
    .AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);

Tente com um novo projeto e compare as diferenças.


11
O momento principal aqui é reinstalar a versão adequada do Microsoft.AspNetCore.Mvc.NewtonsoftJson Não prestei atenção na versão, pois este pacote estava disponível na caixa sem erros e avisos! Obrigado pela resposta! Tudo funciona exatamente como eu esperava!
— Nazar Pylyp 6/12/19

11
Não é errado que, com o aprimoramento do desempenho do sistema json, tenhamos que usar o NewtonsoftJson? : /
— Marek Urbanowicz 04/04

40

.NET Core 3.1 Instale o pacote Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs Adicionar serviço

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

11
Você pode formatar sua resposta e adicionar alguns detalhes? É ilegível.
— Sid

Para mais detalhes, consulte: thecodebuzz.com/…
— Diego Venâncio

4

A configuração das opções de serialização JSON na inicialização é provavelmente a maneira preferida, pois você provavelmente terá casos semelhantes no futuro. Enquanto isso, entretanto, você pode tentar adicionar atributos de dados ao seu modelo para que ele não seja serializado: https://www.newtonsoft.com/json/help/html/PropertyJsonIgnore.htm

public class Reservation{ 
    public int ReservationId {get;set;} 
    public int RestaurantId {get;set;} 
    [JsonIgnore]
    public Restaurant Restaurant {get;set;} 
}

Isso também funciona. Mas, como você mencionou, com isso você deve atualizar todos os modelos, prefiro services.AddControllers (). AddNewtonsoftJson (options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
— Nantharupan 22/02

1
public class Reservation{ 
public int ReservationId {get;set;} 
public int RestaurantId {get;set;} 
[JsonIgnore]
public Restaurant Restaurant {get;set;} 

Acima funcionou também. Mas eu prefiro o seguinte

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

Como primeiro precisamos adicionar o atributo a todos os modelos, podemos ter a referência cíclica.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.