調整 Controller 的 Json 序列化設定 (JsonSerializerOptions
)
這是效能最差最差的解法,拜託不要這樣用,寫給你看不是要你學,是提醒你不要這樣寫!
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
這種寫法的優點是「不會出現例外」,而且你在不特別加入 [JsonIgnore]
的情況下,你的 API 就可以順利輸出 JSON 結果。你知道的,有些人要求不多,只求程式能動就好,那這招你可以參考,快,又有效!XD
這種寫法的缺點是「效能極差」,而且可能會浪費大量頻寬,產生許多無意義的關聯資料。
你直接看執行結果就知道了,許多無意義的循環參考資料都被加入,非常沒有效率:
[
{
"courseId": 1,
"title": "Entity Framework 6 開發實戰",
"credits": 1,
"departmentId": 5,
"department": {
"departmentId": 5,
"name": "專案開發部",
"budget": 1000,
"startDate": "2022-04-20T23:38:36.3721319",
"instructorId": null,
"instructor": null,
"courses": [
null,
{
"courseId": 2,
"title": "Git新手入門",
"credits": 1,
"departmentId": 5,
"department": null,
"enrollments": [],
"instructors": []
},
{
"courseId": 3,
"title": "Git進階版控流程",
"credits": 2,
"departmentId": 5,
"department": null,
"enrollments": [],
"instructors": []
}
]
},
"enrollments": [],
"instructors": []
},
{
"courseId": 2,
"title": "Git新手入門",
"credits": 1,
"departmentId": 5,
"department": {
"departmentId": 5,
"name": "專案開發部",
"budget": 1000,
"startDate": "2022-04-20T23:38:36.3721319",
"instructorId": null,
"instructor": null,
"courses": [
{
"courseId": 1,
"title": "Entity Framework 6 開發實戰",
"credits": 1,
"departmentId": 5,
"department": null,
"enrollments": [],
"instructors": []
},
null,
{
"courseId": 3,
"title": "Git進階版控流程",
"credits": 2,
"departmentId": 5,
"department": null,
"enrollments": [],
"instructors": []
}
]
},
"enrollments": [],
"instructors": []
},
{
"courseId": 3,
"title": "Git進階版控流程",
"credits": 2,
"departmentId": 5,
"department": {
"departmentId": 5,
"name": "專案開發部",
"budget": 1000,
"startDate": "2022-04-20T23:38:36.3721319",
"instructorId": null,
"instructor": null,
"courses": [
{
"courseId": 1,
"title": "Entity Framework 6 開發實戰",
"credits": 1,
"departmentId": 5,
"department": null,
"enrollments": [],
"instructors": []
},
{
"courseId": 2,
"title": "Git新手入門",
"credits": 1,
"departmentId": 5,
"department": null,
"enrollments": [],
"instructors": []
},
null
]
},
"enrollments": [],
"instructors": []
}
]
調整實體模型的導覽屬性,特別加上 [JsonIgnore]
屬性 (Attribute)
public partial class Course
{
public Course()
{
Enrollments = new HashSet<Enrollment>();
Instructors = new HashSet<Person>();
}
public int CourseId { get; set; }
public string? Title { get; set; }
public int Credits { get; set; }
public int DepartmentId { get; set; }
[JsonIgnore] // <-- 加上這行
public virtual Department Department { get; set; } = null!;
[JsonIgnore] // <-- 加上這行
public virtual ICollection<Enrollment> Enrollments { get; set; }
[JsonIgnore] // <-- 加上這行
public virtual ICollection<Person> Instructors { get; set; }
}
在執行一次,就可以得到相當乾淨的 JSON 結果:
[
{
"courseId": 1,
"title": "Entity Framework 6 開發實戰",
"credits": 1,
"departmentId": 5
},
{
"courseId": 2,
"title": "Git新手入門",
"credits": 1,
"departmentId": 5
},
{
"courseId": 3,
"title": "Git進階版控流程",
"credits": 2,
"departmentId": 5
}
]
這種寫法的優點是「輸出的 JSON 結果很乾淨,也節省網路頻寬,下載速度快」,而且你的 API 也可以順利輸出 JSON 結果!
這種寫法的缺點是「需要手動調整實體模型」,而且你若用 DB First 的方式自動產生實體模型,下次在重新產生實體模型時可能會蓋掉你自己手動加上的 [JsonIgnore]
屬性 (Attribute),這個層次的程式維護性是需要考慮的!
要解決這個缺點,可以考慮用使用自訂 ViewModel 的方式,將 API 回應 JSON 部分避開「循環參考」等問題。
透過 Microsoft.AspNetCore.Mvc.NewtonsoftJson
套件搭配 MetadataType
在另一個型別套用 [JsonIgnore]
屬性 (Attribute)
先加入 Microsoft.AspNetCore.Mvc.NewtonsoftJson
套件
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
調整 Program.cs
的 builder.Services.AddControllers()
內容:
builder.Services.AddControllers().AddNewtonsoftJson();
加入一個 Course.Partial.cs
檔案,內容如下,但重點是你不能用 ASP.NET Core MVC 的 ModelMetadataType
屬性 (Attribute),必須改用 System.ComponentModel.DataAnnotations
下的 MetadataType
屬性 (Attribute),而且 [JsonIgnore]
屬性 (Attribute) 要用 Newtonsoft.Json
命名空間下的才行:
using System.ComponentModel.DataAnnotations;
namespace EFCore6Demo.Models;
[MetadataType(typeof(CourseMetadata))]
public partial class Course
{
}
internal class CourseMetadata
{
public int CourseId { get; set; }
public string? Title { get; set; }
public int Credits { get; set; }
public int DepartmentId { get; set; }
[Newtonsoft.Json.JsonIgnore]
public virtual Department Department { get; set; } = null!;
[Newtonsoft.Json.JsonIgnore]
public virtual ICollection<Enrollment> Enrollments { get; set; }
[Newtonsoft.Json.JsonIgnore]
public virtual ICollection<Person> Instructors { get; set; }
}
這種寫法的優點是輸出的 JSON 結果很乾淨,你的 API 也可以順利輸出 JSON 結果,而且就算你用 DB First 的方式自動產生實體模型,下次在重新產生實體模型時也不會蓋掉你自己手動加上的 [JsonIgnore]
屬性 (Attribute),可以兼具效率與程式維護性!
這種寫法的缺點是必須繼續使用 Newtonsoft.Json
(JSON.NET) 來序列化回應的資料,無法享受 System.Text.Json
命名空間下的類別帶來的效能優勢!