.Net Core 3.0 обнаружен возможный цикл объекта, который не поддерживается

22

У меня есть 2 лица, которые связаны как один ко многим

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

Если я пытаюсь получить рестораны с оговорками, используя мой API

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

Я получаю сообщение об ошибке, потому что объекты содержат ссылки друг на друга. Есть похожие посты, которые рекомендуют создать отдельную модель или добавить конфигурацию NewtonsoftJson

Проблема в том, что я не хочу создавать отдельную модель, и второе предложение не помогло. Есть ли способ загрузить данные без циклических отношений? *

System.Text.Json.JsonException: обнаружен возможный цикл объекта, который не поддерживается. Это может происходить из-за цикла или если глубина объекта превышает максимально допустимую глубину 32. в System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected (Int32 maxDepth) в System.Text.Json.JsonSerializer.Write (Utf8JsonWriter writer , Int32 originalWriterDepth, Int32 flushThreshold, параметры JsonSerializerOptions, WriteStack & state) в System.Text.Json.JsonSerializer.WriteAsyncCore (поток utf8Json, значение объекта, тип inputType, параметры JsonSerializerOptions, CancellationToken.Tat.ToTone.Out.ToNet.ToreStore.ForS). В Microsoft WriteResponseBodyAsync (контекст OutputFormatterWriteContext, кодировка selectedEncoding) в Microsoft.AspNetCore.Mvc.

*

Назар Пилип
источник
Попросите его игнорировать свойство Restaurant класса Reservation.
Лассе В. Карлсен
6
На самом деле вы не должны возвращать свои объекты БД непосредственно из вашего API. Я бы предложил создать DTO, специфичные для API, и отобразить их соответственно. Конечно, вы сказали, что не хотите этого делать, но я бы посчитал хорошей практикой разделять внутренние компоненты API и персистентности.
Маки
«и второе предложение не помогло» нуждается в деталях.
Хенк
«Проблема в том, что я не хочу создавать отдельную модель». Ваш дизайн в корне ошибочен, если только вы не сделаете это. API представляет собой контракт , как интерфейс (это буквально прикладное программирование интерфейс ). Он никогда не должен изменяться после публикации, и любое изменение требует новой версии, которая должна работать одновременно со старой версией (которая будет устарела и в конечном итоге будет удалена). Это позволяет клиентам своевременно обновлять свои реализации. Если вы возвращаете сущность напрямую, вы тесно связываете свой уровень данных.
Крис Пратт
Любое изменение в этом уровне данных требует немедленного и необратимого изменения API, немедленно ломая всех клиентов, пока они не обновят свои реализации. Если это не очевидно, это плохо. Вкратце: никогда не принимайте и не возвращайте сущности из API. Вы всегда должны использовать DTO.
Крис Пратт

Ответы:

33

Я попробовал ваш код в новом проекте, и второй способ, кажется, хорошо работает после установки пакета Microsoft.AspNetCore.Mvc.NewtonsoftJson сначала для 3.0

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

Попробуйте с новым проектом и сравните различия.

Райан
источник
1
Ключевым моментом здесь является переустановка правильной версии Microsoft.AspNetCore.Mvc.NewtonsoftJson Я не обращал внимания на версию, так как этот пакет был доступен в коробке без каких-либо ошибок и предупреждений! Спасибо за ответ ! Все работает именно так, как я ожидал!
Назар Пилип
1
Не правда ли, что с улучшенной производительностью системы json нам придется использовать NewtonsoftJson? : /
Марек Урбанович
41

.NET Core 3.1 Установите пакет Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs Добавить сервис

services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
anjoe
источник
1
Можете ли вы отформатировать свой ответ и добавить некоторые детали? Это нечитаемо
Сид
Для более подробной информации, проверьте: thecodebuzz.com/…
Диего Венансио
4

Использование параметров сериализации JSON при запуске, вероятно, является предпочтительным способом, поскольку в будущем у вас, вероятно, будут похожие случаи. Тем временем, однако, вы можете попробовать добавить атрибуты данных в вашу модель, чтобы она не сериализовалась: 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;} 
}
тимур
источник
Это тоже работает. Но, как вы упомянули, с этим вы должны обновить все модели, я предпочитаю services.AddControllers (). AddNewtonsoftJson (options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Нантарупан
1
public class Reservation{ 
public int ReservationId {get;set;} 
public int RestaurantId {get;set;} 
[JsonIgnore]
public Restaurant Restaurant {get;set;} 

Выше работал также. Но я предпочитаю следующее

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

Поскольку сначала нам нужно добавить атрибут ко всем моделям, мы можем иметь циклическую ссылку.

Nantharupan
источник