处理循环引用的更好方法? - EF Core 3.1 和 Web API

Better Way of Dealing With Circular References? - EF Core 3.1 and Web API

我目前正在尝试通过一对多(user 有很多 items)来推进 EF Core。一三个教程之后,我设法让事情与两个非常小和简单的表一起工作;然而,我得到了一个 json 异常:A possible object cycle was detected which is not supported 这表明我有循环引用。

这是我使用 DTO 对象解决问题的代码,但是有没有更简洁的方法可以解决这个问题,因为输入虽然有效,但感觉有点不对。

用户:

namespace TestWebApplication.Database
{
    public class User
    {
        [Key]
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string Dob { get; set; }
        public string Location { get; set; }
        public ICollection<Items> Items { get; set; }
    }
} 

项目:

namespace TestWebApplication.Database
{
    public class Items
    {
        [Key]
        public int ItemId { get; set; }
        public string Item { get; set; }
        public string Category { get; set; }
        public string Type { get; set; }
        public virtual User User { get; set; }
    }
}

DtoItems:

namespace TestWebApplication.Database.DTOs
{
    public class DtoItems
    {
        public string Item { get; set; }
        public string Category { get; set; }
        public string Type { get; set; }
        public DtoUser User { get; set; }
    }
}

DtoUser:

namespace TestWebApplication.Database.DTOs
{
    public class DtoUser
    {
        public string UserName { get; set; }
        public string Dob { get; set; }
        public string Location { get; set; }
    }
}

测试控制器:

[HttpGet]
[Route("getitems")]
public ActionResult<List<Items>> GetItems()
{
    List<Items> items = _myContext.Items.Include(i => i.User).ToList();

    // DTOs
    List<DtoItems> dtoItems = new List<DtoItems>();

    foreach (var i in items)
    {
        var dtoItem = new DtoItems
        {
            Item = i.Item,
            Category = i.Category,
            Type = i.Type,
            User = new DtoUser
            {
                UserName = i.User.UserName,
                Dob = i.User.Dob,
                Location = i.User.Location
            }
        };

        dtoItems.Add(dtoItem);
    }

    return Ok(dtoItems);
}

端点的输出:

[
    {
        "item": "xxx",
        "category": "xxx",
        "type": "xxx",
        "user": {
            "userName": "xxx",
            "dob": "xxx",
            "location": "xx"
        }
    },
    {
        "item": "xxx",
        "category": "xxx",
        "type": "xxx",
        "user": {
            "userName": "xxx",
            "dob": "xxx",
            "location": "xxx"
        }
    }
]

在我看来,使用 DTO 是处理此问题的正确方法。您的数据模型不能很好地序列化这一事实试图向您暗示您根本不应该从 API 序列化您的数据模型。

我认为 returning DTO 也解决了未来的进一步问题(如果你想 return UserModel 的所有属性,除了一个,也许它是一个敏感的 属性您不想只从 API 中 return,如果数据库中的 UserModel 获得更多您不想 return 的导航属性怎么办?)

实际上只有另外两种处理方法。

  • 您可以切换到 Newtonsoft.Json,它支持处理引用循环,您可以在一行中配置它

像这样:

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

System.Text.Json 不支持这样做(目前)。请关注此 Github 问题以获取更多信息:https://github.com/dotnet/runtime/issues/30820

  • 您使用 JsonIgnore 属性强制对属性进行非序列化,这将起作用,但是...让 EntityFramework 模型具有 JSON 序列化选项看起来很奇怪...

所以最好的选择是坚持使用 DTO。

更多信息: