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

ASP.NET Core MVC 8添加商品到购物车触发InvalidOperationException报错

ASP.NET Core MVC 8 购物车添加功能报错解决

我在ASP.NET Core MVC 8项目中实现商品添加至购物车功能,需求为:将指定商品加入购物车,若购物车条目不存在则创建并入库;重复添加同一件商品时数量加1。但操作时触发以下错误:

异常信息:CLR/System.InvalidOperationException
类型为'System.InvalidOperationException'的异常在System.Private.CoreLib.dll中抛出,未被用户代码处理:'Invalid operation. The connection is closed.'

相关代码

ShoppingCartController.cs

private readonly ILogger<ShoppingCartController> _logger;
private readonly DBContext _dbContext;
private const string CartSessionKey = "CartId";
private string ShoppingCartId { get; set;}

public ShoppingCartController(ILogger<ShoppingCartController> logger, DBContext dBContext)
{
    _logger = logger;
    _dbContext = dBContext;
}

public async void AddToCart(int id)
{
    ShoppingCartId = GetCartID();
    // 该行代码触发InvalidOperationException
    Cart cart = await _dbContext.Carts.FirstOrDefaultAsync(c => c.CartId == ShoppingCartId && c.ProductId == id);
    if (cart == null)
    {
        // 若购物车不存在则创建新购物车
        Cart newCart = new Cart
        {
            ProductId = id,
            CartId = ShoppingCartId,
            Product = await _dbContext.Products.FirstOrDefaultAsync(p => p.ProductId == id),
            Count = 1,
            CreatedDate = DateTime.Now,
        };

        await _dbContext.Carts.AddAsync(newCart);
    }
    else
    {
        cart.Count++;
    }

    await _dbContext.SaveChangesAsync();
}

private string GetCartID()
{
    if(HttpContext.Session.Keys.Contains(CartSessionKey) == false)
    {
        if (!string.IsNullOrWhiteSpace(HttpContext.User.Identity.Name))
        {
            HttpContext.Session.SetString(CartSessionKey,HttpContext.User.Identity.Name);
        }
        else
        { 
            Guid tempCartId = Guid.NewGuid();
            HttpContext.Session.SetString(CartSessionKey, tempCartId.ToString());
        }
    }

    return HttpContext.Session.GetString(CartSessionKey);
}

Dashboard.cshtml

<div class="d-flex">
    <div class="row rows-cols-4">
        @foreach(var prod in @ViewBag.Products)
        {
            <div class="card m-1" style="width: 18rem;">
                <img src="..." class="card-img-top" alt="...">
                <div class="card-body">
                    <h5 class="card-title">@prod.ProductName</h5>
                    <a class="btn btn-primary" asp-controller="Customer" asp-action="ProductDetails" asp-route-id="@prod.ProductId">More Details</a>
                    <!-- 点击此按钮添加商品到购物车 -->
                    <a class="btn btn-primary" asp-controller="ShoppingCart" asp-action="AddToCart" asp-route-id="@prod.ProductId">Add to cart</a>
                </div>
            </div>
        }
        
    </div>
</div>

Cart.cs

public class Cart
{
    public int Id { get; set; }

    public int Count { get; set; }

    public string CartId { get; set; }

    public DateTime CreatedDate { get; set; }

    public int ProductId { get; set; }

    public virtual Product Product { get; set; }
}

报错原因及解决方法

1. 核心问题:使用async void作为控制器动作方法返回类型

ASP.NET Core控制器动作方法中使用async void会导致请求上下文提前释放,DbContext的数据库连接也会被关闭,进而触发"连接已关闭"的异常。必须改为返回async Task<IActionResult>:

修改AddToCart方法签名及内容:

public async Task<IActionResult> AddToCart(int id)
{
    ShoppingCartId = GetCartID();
    Cart cart = await _dbContext.Carts.FirstOrDefaultAsync(c => c.CartId == ShoppingCartId && c.ProductId == id);
    if (cart == null)
    {
        Cart newCart = new Cart
        {
            ProductId = id,
            CartId = ShoppingCartId,
            // 不需要手动赋值Product导航属性,EF会通过ProductId自动关联
            Count = 1,
            CreatedDate = DateTime.Now,
        };
        await _dbContext.Carts.AddAsync(newCart);
    }
    else
    {
        cart.Count++;
    }
    await _dbContext.SaveChangesAsync();
    // 添加重定向或返回结果,比如回到商品列表页
    return RedirectToAction("Dashboard", "Customer");
}

2. 移除不必要的导航属性赋值

创建Cart对象时手动查询Product并赋值是多余的,EF Core会通过ProductId自动维护导航属性,这不仅节省数据库查询,还能避免潜在的上下文问题。

3. 确认Session配置正确

确保在Program.cs中已经配置了Session服务,否则GetCartID方法无法正常工作:

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

// 中间件配置要放在UseRouting之后,UseEndpoints之前
app.UseSession();

4. 确保DbContext生命周期配置正确

默认情况下DBContext是作用域生命周期(Scoped),这符合控制器的生命周期,不要将其注册为单例(Singleton),否则会导致连接复用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 18:53:14