提问人:Yusuf 提问时间:10/25/2023 更新时间:10/25/2023 访问量:48
修复 C# Web API 中的循环引用错误
Fixing Circular Reference Error in C# Web API
问:
我有一个用 C# 编写的相当简单的 Web API,使用 EF。这个想法几乎是一个待办事项应用程序,其具体关系是:一个用户可以有许多待办事项列表,每个待办事项列表可以有许多待办事项。
以下是我的数据模型:
public class User
{
public Guid Id { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public string? Password { get; set; } // Be sure to properly hash and salt passwords
public string? ProfilePhotoUrl { get; set; } // Store the URL to the user's profile photo
public List<ToDoList>? ToDoLists { get; set; } // A user can have multiple to-do lists
}
public class ToDoList
{
public Guid Id { get; set; }
public string? Title { get; set; }
public List<ToDo>? ToDos { get; set; } // Each to-do list contains multiple to-dos
// Foreign Key to User
public Guid UserId { get; set; }
public User? User { get; set; }
}
public class ToDo
{
public Guid Id { get; set; }
public string? Name { get; set; }
public string? Description { get; set; }
public bool? InProgress { get; set; }
public bool? IsComplete { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public ToDoList? ToDoList { get; set; }
public Guid? ToDoListId{ get; set; }
}
我有一个控制器,允许用户注册、登录等。我想做的一件事是检索用户可能拥有的每个待办事项列表。我在服务中有代码:
public Task<User> GetToDoListsForUsers(Guid userId)
{
return _context.Users
.Include(u => u.ToDoLists)
.FirstOrDefaultAsync(u => u.Id == userId);
}
然后由控制器调用:
[HttpGet("{userId}/todolists")]
public async Task<ActionResult<IEnumerable<ToDoList>>> GetToDoListsForUser(Guid userId)
{
var user = await _userService.GetToDoListsForUsers(userId);
if (user == null)
{
return NotFound("User not found");
}
var toDoLists = user.ToDoLists;
if (toDoLists.Count == 0)
{
return NotFound("user has no lists");
}
return Ok(toDoLists);
}
但是,当我运行它时,我收到以下错误:
An unhandled exception has occurred while executing the request.
System.Text.Json.JsonException: A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. Path: $.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.User.ToDoLists.Id.
我相信这是因为 User 具有对 ToDoList 的引用,而 ToDoList 具有对 User 的引用,但我需要这两种方式的引用,因为它们是外键。
我还在网上遇到了这个解决方案,以添加以下几行:
var jsonSettings = new JsonSerializerOptions
{
ReferenceHandler = ReferenceHandler.Preserve,
};
var serializedUser = JsonSerializer.Serialize(user, jsonSettings);
var deserializedUser = JsonSerializer.Deserialize<User>(serializedUser, jsonSettings);
var toDoLists = deserializedUser.ToDoLists;
到控制器方法,但这也不起作用。
在解决这个问题方面有什么帮助吗?
答:
1赞
Guru Stron
10/25/2023
#1
TBH 找到的“解决方案”没有任何意义,使用相同的设置只会恢复引用。ReferenceHandler.Preserve
您可以只返回序列化的手动数据:
var serializedLists = JsonSerializer.Serialize(user.ToDoLists, jsonSettings);
return Content(serializedLists);
或者更好地全局应用设置:ReferenceHandler.Preserve
builder.Services.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);
另一个值得考虑的选项(我默认使用的一个) - 创建 DTO 并映射到它们,因此不存在循环,响应只有所需的数据。
阅读更多内容 - 避免或控制 Entity Framework Core 中的循环引用。
评论
1赞
Steve Py
10/25/2023
我建议使用 DTO 进行数据传输,而不是尝试发送实体。循环引用在数据域 PoV 中很有用,但在序列化 PoV 中是无用且有问题的,因此最好使用适合特定目的的类。有了序列化程序跟踪引用,并确保不编写循环引用,而只依次引用。这将产生遍历所有参考的成本,如果不需要或没有用,则可以避免使用 DTO 进行引用。ReferenceHandler.Preserve
0赞
Yusuf
10/26/2023
谢谢 - 将其添加到我的程序 .cs 中解决了这个问题,现在一切正常。Tbh - 我以前没有遇到过 DTO,但阅读它们绝对是处理这个问题的更好方法,也可能实现它。
0赞
Guru Stron
10/26/2023
@Yusuf很乐意帮忙!如果答案适合您 - 请随时投票并接受(左侧的复选标记)
评论