.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 ที่ , Int32 originalWriterDepth, Int32 flushThreshold, ตัวเลือก JsonSerializerOptions, WriteStack & State) ที่ System.Text.Json.Json.JsonSerializer.WriteAsyncCore (สตรีม utf8Json, ค่าออบเจ็กต์, อินพุตชนิด, JsonSerializerOptions ตัวเลือก WriteResponseBodyAsync (บริบท OutputFormatterWriteContext, การเข้ารหัสการเข้ารหัสที่เลือก) ที่ Microsoft.AspNetCore.Mvc

* * * *


ขอให้เพิกเฉยต่อคุณสมบัติของร้านอาหารในคลาสการจอง
Lasse V. Karlsen

6
จริงๆแล้วคุณไม่ควรส่งคืนเอนทิตี DB ของคุณโดยตรงจาก API ของคุณ ฉันขอแนะนำให้สร้าง DTO เฉพาะ API และการจับคู่ตามลำดับ ได้รับคุณบอกว่าคุณไม่ต้องการทำเช่นนั้น แต่ฉันคิดว่าเป็นแนวปฏิบัติทั่วไปที่ดีในการแยก API และการคงอยู่ภายในไว้
mackie

"และข้อเสนอแนะที่สองไม่ได้ช่วย" ต้องการรายละเอียด
Henk Holterman

"ปัญหาคือฉันไม่ต้องการสร้างแบบจำลองแยกต่างหาก" การออกแบบของคุณมีข้อบกพร่องพื้นฐานเว้นแต่คุณจะทำแค่นั้น API ที่เป็นสัญญาเช่นอินเตอร์เฟซ (มันอย่างแท้จริงการเขียนโปรแกรมอินเตอร์เฟซ ) ไม่ควรเปลี่ยนแปลงเผยแพร่ครั้งเดียวและการเปลี่ยนแปลงใด ๆ จำเป็นต้องมีเวอร์ชันใหม่ซึ่งจะต้องใช้งานพร้อมกันกับเวอร์ชันเก่า (ซึ่งจะเลิกใช้แล้วและถูกลบออกในอนาคต) ที่ช่วยให้ลูกค้ามีเวลาในการปรับปรุงการใช้งานของพวกเขา หากคุณส่งคืนเอนทิตีโดยตรงแสดงว่าคุณกำลังเชื่อมโยงชั้นข้อมูลของคุณแน่น
Chris Pratt

การเปลี่ยนแปลงใด ๆ ในชั้นข้อมูลนั้นจำเป็นที่จะต้องมีการเปลี่ยนแปลง API แบบทันทีและไม่สามารถย้อนกลับได้ทำให้ลูกค้าทั้งหมดหยุดงานทันทีจนกว่าพวกเขาจะอัปเดตการใช้งาน ในกรณีที่ไม่ชัดเจนนั่นเป็นสิ่งที่ไม่ดี กล่าวโดยย่อ: ห้ามรับหรือส่งเอนทิตีจาก API คุณควรเสมอใช้ DTOs
Chris Pratt

คำตอบ:


32

ฉันลองใช้รหัสของคุณในโครงการใหม่และวิธีที่สองดูเหมือนจะทำงานได้ดีหลังจากติดตั้งแพคเกจMicrosoft.AspNetCore.Mvc.NewtonsoftJson ในตอนแรกสำหรับ 3.0

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

ลองกับโครงการใหม่และเปรียบเทียบความแตกต่าง


1
ช่วงเวลาสำคัญที่นี่คือการติดตั้ง Microsoft.AspNetCore.Mvc.NewtonsoftJson รุ่นที่เหมาะสมอีกครั้งฉันไม่ได้สนใจรุ่นนี้เนื่องจากแพ็คเกจนี้มีให้ในกล่องโดยไม่มีข้อผิดพลาดและคำเตือน! ขอบคุณสำหรับคำตอบ ! ทุกอย่างทำงานตรงตามที่ฉันคาดไว้!
Nazar Pylyp

1
มันไม่ผิดหรอกที่ระบบ json ที่ปรับปรุงแล้วเราต้องใช้ NewtonsoftJson หรือไม่ : /
Marek Urbanowicz

40

.NET Core 3.1 ติดตั้งแพคเกจ Microsoft.AspNetCore.Mvc.NewtonsoftJson

Startup.cs เพิ่มบริการ

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

1
คุณสามารถจัดรูปแบบการตอบกลับและเพิ่มรายละเอียดได้ไหม? มันอ่านไม่ได้
Sid

สำหรับรายละเอียดเพิ่มเติมให้ตรวจสอบ: thecodebuzz.com/…
Diego Venâncio

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

เพราะก่อนอื่นเราต้องเพิ่มคุณสมบัติให้กับทุกรุ่นเราอาจมีการอ้างอิงแบบวน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.