You need to enable JavaScript to run this app. EF Core 5 含SUM逻辑的Gross计算属性导致应用启动无报错退出
优惠活动
大模型
产品
解决方案
定价
更多

EF Core 5 含SUM逻辑的Gross计算属性导致应用启动无报错退出

EF Core 5 实体计算属性引发应用无提示崩溃问题排查

问题背景

在基于EF Core 5开发的销售单模块中,定义了带计算属性的销售单、销售单明细实体,配置完成后出现应用启动即直接退出、Visual Studio未弹出任何错误提示的异常。移除SalesOrder实体中的Gross计算属性后,应用可正常运行,现有查询逻辑也可正常执行。

涉及的实体定义代码

销售单明细实体

public class SalesOrderItem
{
    public int SalesOrderItemId { get; set; }

    public int? Qty { get; set; }

    public decimal? UnitPrice { get; set; }
    
    public string Description { get; set; }

    [ForeignKey("SalesOrderId")]
    public int SalesOrderId { get; set; }

    [NotMapped]
    public decimal LineTotal => (Qty == null ? 0 : Qty.Value) * (UnitPrice == null ? 0 : UnitPrice.Value); 
}

销售单实体

public class SalesOrder
{
    public int SalesOrderId { get; set; }

    public int VatCodeId { get; set; }

    public virtual IEnumerable<SalesOrderItem> SalesOrderItems { get; set; }

    [NotMapped]
    public decimal? Net
    {
        get
        {
            if (SalesOrderItems == null)
                return null;

            return SalesOrderItems.Sum(p => p.LineTotal);
        }
    }

    [NotMapped]
    public decimal? Tax
    {
        get
        {
            if (Net == null)
                return null;

            return (Net.Value * VatCode.Percentage) / 100;
        }
    }

    [NotMapped]
    public decimal? Gross
    {
        get
        {
            if (Net == null || VatCode == null)
                return null;

            return Net.Value + Gross.Value;
        }
    }
}

数据查询逻辑

var salesOrder = await _context.SalesOrder
    .Include(p => p.VatCode)
    .Include(i => i.SalesOrderItems)
    .SingleOrDefaultAsync(i => i.SalesOrderId == salesOrderId);

崩溃根因

  • 核心触发点:Gross属性无限递归导致栈溢出
    Gross属性的getter逻辑中出现笔误,将返回值写为Net.Value + Gross.Value,相当于每次读取Gross属性时,都会再次触发自身的getter执行,形成无终止的自我递归调用,快速占满进程调用栈,触发栈溢出异常。
    栈溢出是.NET运行时层面的严重异常,无法通过普通try-catch逻辑捕获,触发后会直接终止进程;如果Visual Studio未配置针对栈溢出的异常调试断点,就会表现为应用无任何错误提示直接退出。
  • 附带的潜在运行隐患(非本次崩溃的直接原因,但会引发后续业务异常)
    • Tax属性的getter直接访问VatCode.Percentage,未对VatCode做空值判断,当VatCode未完成加载、实体处于断连状态时,会直接触发空引用异常
    • SalesOrderItems集合定义为IEnumerable<SalesOrderItem>,虽然EF Core可以正常完成数据填充,但后续业务场景中对集合的新增、删除操作会出现兼容问题,更推荐使用ICollection<SalesOrderItem>类型定义导航属性

修复方案

  1. 修正Gross属性的递归逻辑,按照业务语义,含税总额应为净额加税额,而非自身加自身
  2. 为Tax属性补充VatCode的空值判断,避免空引用
  3. 优化导航属性的类型定义
    修正后的核心计算属性代码:
[NotMapped]
public decimal? Tax
{
    get
    {
        if (Net == null || VatCode == null)
            return null;
        return (Net.Value * VatCode.Percentage) / 100;
    }
}

[NotMapped]
public decimal? Gross
{
    get
    {
        if (Net == null || Tax == null)
            return null;
        return Net.Value + Tax.Value;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.05 21:06:10