【问题标题】:Better Way of Dealing With Circular References? - EF Core 3.1 and Web API处理循环引用的更好方法? - EF Core 3.1 和 Web API
【发布时间】:2020-03-07 16:56:12
【问题描述】:

我目前正在尝试以一对多的方式使用 EF Core(user 有很多 items)。一个或三个教程之后,我设法让两个非常小而简单的表工作起来;但是,我得到了一个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"
        }
    }
]

【问题讨论】:

  • 序列化问题应该在那里解决。 EF Core 无济于事,因为这不是他们关心的问题(EF Core 可以毫无问题地处理循环引用)。见Related data and serialization
  • @IvanStoev 这很有帮助。我在User 类中尝试了ICollection&lt;Items&gt; 上方的[JsonIgnore] (system.text.json) 属性,它完成了与我编写的DTO 代码相同的工作!如果它在某种引用循环中,我担心其他问题,例如内存中的无限数量的对象?
  • 假设您有 1 个 User 和 5 个 Items。这 5 个Items 中的每一个的User 属性都指向相同 1 个User 实例。所以最后你只有 6 个对象,即根本没有额外数量的对象。
  • 啊,好吧,现在更有意义了。我尝试尝试的一件事是首先使用 startup.csRelated Data and Serialization 示例中的代码和 Microsoft.AspNetCore.Mvc.NewtonsoftJson,这样做是给我一个空的 items 数组user 响应中的对象。所以[JsonIgnore] 最适合我。谢谢。
  • 顺便说一句,不要误会我的意思。解决循环引用问题并不能解决所有序列化问题(例如,如果启用了延迟加载)。与直接使用实体相比,使用 DTO 可以更好地控制来回发送的确切内容,尤其是对于返回数据。例如,如果一个相同的数据库上下文实例用于多个调用,则返回的实体可能包含相关数据,即使它没有被明确请求。

标签: asp.net-core ef-core-3.0


【解决方案1】:

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

我认为返回 DTO 还可以解决进一步的问题(如果您想返回 UserModel 的所有属性,除了一个,也许它是您不想从 API 返回的敏感属性,如果您的数据库中的 UserModel 获得了更多您不想返回的导航属性?)。

实际上只有两种其他方法可以处理此问题。

  • 您可以切换到支持处理引用循环的 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。

更多信息:

【讨论】:

  • 更新:System.Text.Json 现在也可以处理循环:JsonSerializer.Serialize(command, new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve })
猜你喜欢
  • 2011-12-27
  • 2020-04-15
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 2018-12-17
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多