You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core多对多关联表操作触发栈溢出及访问冲突异常求助

解决多对多关联时的Stack Overflow与Access Violation异常

问题概述

每次尝试关联多对多关系的相关表时,程序触发**Stack Overflow(栈溢出)和Access Violation(访问冲突)**异常,尽管数据已成功插入数据库,但程序仍会崩溃。

错误日志摘要

调试控制台输出:

info: Microsoft.Hosting.Lifetime[0] Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0] Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0] Content root path: C:\Users\vynar\source\repos\IndividualProjects\E_Library\API
Stack overflow.
C:\Users\vynar\source\repos\IndividualProjects\E_Library\API\bin\Debug\netcoreapp3.1\ELibrary.API.exe (process 14384) exited with code -1073741819.

VS输出窗口关键信息:

The program '[14384] ELibrary.API.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.

问题根源

从你的代码来看,问题出在序列化循环引用上:

  1. 你在Attach方法中,给book.AuthorBooks和author.AuthorBooks都添加了authorBook实例
  2. 当控制器通过CreatedAtAction返回authorBook时,JSON序列化器会尝试序列化整个对象图:
    • AuthorBook包含Book和Author引用
    • Book又包含AuthorBooks集合,其中又包含当前的AuthorBook
    • Author同理也包含AuthorBooks集合,形成无限递归
  3. 这种无限递归会不断消耗栈内存,最终导致栈溢出和访问冲突

解决方案

方案1:使用DTO(数据传输对象)避免循环引用

不要直接返回实体类AuthorBook,而是创建一个专门的DTO类,只包含需要返回的字段:

// 定义DTO类
public class AuthorBookDto
{
    public int BookId { get; set; }
    public int AuthorId { get; set; }
    // 可以按需添加其他需要返回的字段,比如书名、作者名等
}

然后修改Service和Controller的返回逻辑:

Service层修改:

public async Task<AuthorBookDto> Attach(int bookId, int authorId)
{
    var book = await _unitOfWork.Books.GetByIdAsync(bookId);
    var author = await _unitOfWork.Authors.GetByIdAsync(authorId);
    // 修正判断逻辑:只要其中一个不存在就返回null
    if (book == null || author == null)
    {
        return null;
    }
    var authorBook = new AuthorBook()
    {
        AuthorId = author.Id,
        BookId = book.Id
    };
    book.AuthorBooks.Add(authorBook);
    author.AuthorBooks.Add(authorBook);
    await _unitOfWork.CommitAsync();
    // 转换为DTO返回
    return new AuthorBookDto
    {
        BookId = authorBook.BookId,
        AuthorId = authorBook.AuthorId
    };
}

Controller层修改:

[Authorize(Roles = "Admin, Manager")]
[HttpPost("attach")]
public async Task<IActionResult> Attach([FromBody] AuthorBookModel authorBook)
{
    var createdLink = await _service.Attach(authorBook.BookId, authorBook.AuthorId);
    if (createdLink == null)
    {
        return BadRequest(authorBook);
    }
    return CreatedAtAction(nameof(GetAllBooks), createdLink);
}

方案2:配置JSON序列化器忽略循环引用

如果你坚持要返回实体类,可以在Startup.cs中配置JSON序列化器忽略循环引用:

// 在ConfigureServices方法中添加
services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    });

注意:.NET Core 3.1默认使用System.Text.Json,使用NewtonsoftJson需要先安装Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet包

方案3:在实体类中标记忽略序列化的导航属性

可以通过[JsonIgnore]特性标记不需要序列化的导航属性,避免循环引用:

public class Book
{
    // 其他属性...
    [JsonIgnore]
    public ICollection<AuthorBook> AuthorBooks { get; set; }
}

public class Author
{
    // 其他属性...
    [JsonIgnore]
    public ICollection<AuthorBook> AuthorBooks { get; set; }
}

额外提示

你Service层中的原判断逻辑if (book == null && author == null)存在逻辑错误,应该改为if (book == null || author == null)——只要书籍或作者其中一个不存在,就不应该创建关联,而不是两个都不存在才返回null。

内容的提问来源于stack exchange,提问作者Nazar Vynar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:07:37