.Net Core 3.0 kemungkinan siklus objek terdeteksi yang tidak didukung

22

Saya memiliki 2 entitas yang terkait sebagai satu dengan banyak

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

Jika saya mencoba untuk mendapatkan restoran dengan pemesanan menggunakan api saya

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

Saya menerima kesalahan dalam menanggapi, karena objek berisi referensi satu sama lain. Ada tulisan terkait yang merekomendasikan untuk membuat model terpisah atau menambahkan konfigurasi NewtonsoftJson

Masalahnya adalah saya tidak ingin membuat model terpisah dan saran ke-2 tidak membantu. Apakah ada cara untuk memuat data tanpa hubungan bersepeda? *

System.Text.Json.JsonException: Siklus objek yang mungkin terdeteksi yang tidak didukung. Ini bisa disebabkan oleh siklus atau jika kedalaman objek lebih besar dari kedalaman maksimum yang diizinkan dari 32. di System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerCycleDetected (max32epD Intth) di System.Text.Json.JsonSerializer.Write (Penulisf8Json Penulis , Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack & state) di System.Text.Json.JsonSerializer. WriteResponseBodyAsync (Konteks OutputFormatterWriteContext, Encoding dipilihEncoding) di Microsoft.AspNetCore.Mvc.

*

Nazar Pylyp
sumber
Mintalah untuk mengabaikan properti Restoran dari kelas Reservasi.
Lasse V. Karlsen
6
Sungguh, Anda seharusnya tidak mengembalikan entitas DB Anda langsung dari API Anda. Saya sarankan untuk membuat DTO khusus API dan pemetaan yang sesuai. Memang Anda mengatakan Anda tidak ingin melakukan itu, tetapi saya akan menganggapnya sebagai praktik umum yang baik untuk menjaga API dan internal yang persisten tetap terpisah.
Mackie
"dan saran kedua tidak membantu" membutuhkan detail.
Henk Holterman
"Masalahnya adalah saya tidak ingin membuat model terpisah". Desain Anda pada dasarnya cacat kecuali Anda melakukan hal itu. API adalah kontrak seperti antarmuka (itu benar-benar antarmuka pemrograman aplikasi ). Seharusnya tidak pernah berubah, setelah dipublikasikan, dan perubahan apa pun memerlukan versi baru, yang perlu dijalankan bersamaan dengan versi lama (yang akan ditinggalkan dan akhirnya dihapus di masa depan). Itu memungkinkan klien waktu untuk memperbarui implementasi mereka. Jika Anda mengembalikan suatu entitas secara langsung, Anda dengan erat menggandakan lapisan data Anda.
Chris Pratt
Setiap perubahan pada lapisan data itu kemudian mengharuskan perubahan segera dan tidak dapat diubah ke API, memecah semua klien segera sampai mereka memperbarui implementasinya. Jika tidak jelas, itu hal yang buruk. Singkatnya: tidak pernah menerima atau mengembalikan entitas dari API. Anda harus selalu menggunakan DTO.
Chris Pratt

Jawaban:

32

Saya telah mencoba kode Anda dalam proyek baru dan cara kedua tampaknya bekerja dengan baik setelah menginstal paket Microsoft.AspNetCore.Mvc.NewtonsoftJson pertama untuk 3.0

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

Coba dengan proyek baru dan bandingkan perbedaannya.

Ryan
sumber
1
Momen kunci di sini adalah menginstal ulang versi yang tepat dari Microsoft.AspNetCore.Mvc.NewtonsoftJson Saya tidak memperhatikan versi karena paket ini tersedia di bawah kotak tanpa kesalahan dan peringatan! Terima kasih atas jawabannya ! Semuanya bekerja persis seperti yang saya harapkan!
Nazar Pylyp
1
Bukankah salah bahwa dengan perbaikan perf sistem json, kita harus menggunakan NewtonsoftJson? : /
Marek Urbanowicz
40

.NET Core 3.1 Instal paket Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs Tambahkan layanan

services.AddControllers().AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
anjoe
sumber
1
Bisakah Anda memformat respons Anda dan menambahkan beberapa detail? Itu tidak bisa dibaca.
Sid
Untuk perincian lebih lanjut, periksa: thecodebuzz.com/…
Diego Venâncio
4

Membuat pengaturan opsi serialisasi JSON saat startup agar berfungsi mungkin merupakan cara yang disukai karena Anda mungkin akan memiliki kasus serupa di masa mendatang. Sementara itu, Anda dapat mencoba menambahkan atribut data ke model Anda sehingga tidak bersambung: 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;} 
}
timur
sumber
Ini juga berfungsi. Tetapi seperti yang Anda sebutkan, dengan ini Anda harus memperbarui semua model, saya lebih suka services.AddControllers (). AddNewtonsoftJson (options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
Nantharupan
1
public class Reservation{ 
public int ReservationId {get;set;} 
public int RestaurantId {get;set;} 
[JsonIgnore]
public Restaurant Restaurant {get;set;} 

Di atas juga bekerja. Tapi saya lebih suka yang berikut ini

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

Karena pertama-tama kita perlu menambahkan atribut ke semua model kita mungkin memiliki referensi siklik.

Nantharupan
sumber