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

Hot Chocolate忽略EF Core Include问题及子对象投影方案问询

Hot Chocolate忽略EF Core Include导致计算字段空对象错误的解决方案

问题场景

我搭建了一组模型,需要依赖子对象TransType的字段计算父对象Transaction的Amount属性。模型代码如下:

class Transaction
{
    // PK
    public Guid Id { get; set; }

    // Foreign Key
    public Guid TypeId { get; set; }

    // 记录交易类型:销售、取消、退款等
    public TransType TransactionType { get; set; } 

    // 数据库存储的原始金额始终为正
    private double _amount;

    public double Amount {
        get => _amount;
        set {
            // 根据交易类型的乘数计算对账户的实际影响金额
            _amount = TransactionType.Multiplier * value; 
        }
    }
}

class TransType 
{
    // PK
    public Guid Id { get; set; }
  
    // 取值为1或-1
    [IsProjected(true)]
    public int Multiplier { get; set; }

    [GraphQLIgnore]
    public virtual ICollection<Transaction> Transactions { get; set; }
}

已通过EF Core配置一对多关系并设置AutoInclude确保TransType始终加载:

modelBuilder.Entity<Transaction>(entity =>
{
    entity.HasOne(d => d.TransactionType)
          .WithMany(p => p.Transactions)
          .HasForeignKey(d => d.TypeId);
 
    entity.Navigation(e => e.TransactionType).AutoInclude();
});

同时在GraphQL查询方法中手动添加Include:

[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Transaction> GetTransactions([ScopedService] DbContext db)
{
    return db.Transactions.Include(t => t.TransactionType);
}

问题:查询Transaction的Amount字段时出现空对象错误,原因是GraphQL未请求TransType信息,Hot Chocolate忽略了EF Core的Include配置。希望找到无需静态映射的解决方案,避免后续新增类型时频繁修改代码。

环境版本:.NET 6、Hot Chocolate 12

解决方案

1. 强制GraphQL包含导航属性

在Transaction类的TransactionType属性上添加[GraphQLInclude]特性,强制Hot Chocolate始终加载该导航属性,无论客户端查询是否显式请求:

public class Transaction
{
    // ... 其他属性 ...

    [GraphQLInclude]
    public TransType TransactionType { get; set; } 
}

该方式直接确保TransactionType被加载,让Amount的计算逻辑能正常获取Multiplier值,无需修改查询逻辑。

2. 迁移计算逻辑至数据库层(EF Core计算列)

将Amount的计算逻辑移到数据库层面,通过EF Core配置计算列,彻底规避导航属性依赖:

  1. 调整Transaction模型,新增RawAmount存储数据库原始正值:
class Transaction
{
    // ... 其他属性 ...

    // 数据库存储的原始金额
    public double RawAmount { get; set; }

    // 计算后的实际影响金额
    public double Amount { get; set; }
}
  1. 在EF Core配置中设置计算列:
modelBuilder.Entity<Transaction>(entity =>
{
    // ... 其他关系配置 ...

    // 配置计算列,关联TransType表的Multiplier计算实际金额
    entity.Property(t => t.Amount)
          .HasComputedColumnSql("(SELECT Multiplier FROM TransTypes WHERE Id = TypeId) * [RawAmount]");
});

这种方式由数据库处理计算逻辑,性能更优,且无需依赖导航属性加载。

3. 使用投影拦截器强制添加Include

自定义投影拦截器,在查询构建阶段自动为Transaction添加Include:

public class TransactionProjectionInterceptor : ProjectionInterceptor
{
    public override IQueryable<TEntity> OnBeforeProjection<TEntity>(
        IQueryable<TEntity> query,
        ProjectionContext context)
    {
        if (typeof(TEntity) == typeof(Transaction))
        {
            return (IQueryable<TEntity>)((IQueryable<Transaction>)query).Include(t => t.TransactionType);
        }
        return base.OnBeforeProjection(query, context);
    }
}

然后在服务注册时添加拦截器:

builder.Services.AddGraphQLServer()
    .AddQueryType<Query>()
    .AddProjectionInterceptor<TransactionProjectionInterceptor>()
    // ... 其他GraphQL配置 ...

该方式无需修改模型,通过拦截器全局控制查询逻辑,适合需要保持模型整洁的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 01:00:18