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

EF7:已添加至Context的实体查询返回Null的问题求助

问题

我采用CQRS架构并尝试使用领域事件。在接收新订单命令后,将新建的Order对象添加至DbContext。相关处理代码如下:

public async Task<Guid> Handle(CreateOrderCommand message, CancellationToken cancellationToken)
{
    
    ...

    var order = new Order(...);

    ...

    order.SubmitOrder();

    _orderRepository.Add(order);

    await _orderRepository.UnitOfWork
        .SaveEntitiesAsync(cancellationToken);

    return order.Id;
}

order.SubmitOrder()方法实现如下:

public void SubmitOrder()
{
    AddDomainEvent(new OrderPlacedDomainEvent(Guid.NewGuid(), Id));
}

orderRepository.UnitOfWork.SaveEntitiesAsync是UnitOfWork的重载方法,代码如下:

public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
    // Dispatch Domain Events collection. 
    // Choices:
    // A) Right BEFORE committing data (EF SaveChanges) into the DB will make a single transaction including  
    // side effects from the domain event handlers which are using the same DbContext with "InstancePerLifetimeScope" or "scoped" lifetime
    // B) Right AFTER committing data (EF SaveChanges) into the DB will make multiple transactions. 
    // You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers. 
    if (_mediator != null)
    {
        await _mediator.DispatchDomainEventsAsync(this);
    }

    // After executing this line all the changes (from the Command Handler and Domain Event Handlers) 
    // performed through the DbContext will be committed
    await base.SaveChangesAsync(cancellationToken);
    return true;
}

注:事件分发及处理器调用前并未保存数据。

现在在事件处理器中,尝试从Context获取Order对象:

await _context.Orders.Include(o => o.OrderItems).SingleOrDefaultAsync(o => o.Id == id, cancellationToken: cancellationToken);

该查询返回null,但在_context.ChangeTracker.DebugView.LongView中可看到该数据。请问有办法在此处获取该Order数据吗?

解决方案

问题出在EF Core默认查询会直接访问数据库,而此时Order仅存在于ChangeTracker中,还未持久化到数据库,所以直接查询DbSet返回null。以下几种方式可以获取到目标Order:

方法1:从ChangeTracker中直接筛选

直接遍历ChangeTracker里的已跟踪实体,找到新增状态的Order:

var order = _context.ChangeTracker.Entries<Order>()
    .Where(e => e.Entity.Id == id && e.State == EntityState.Added)
    .Select(e => e.Entity)
    .SingleOrDefault();

// 如果需要关联的OrderItems,确保它们已被跟踪,直接访问实体的OrderItems属性即可
if (order != null)
{
    // 访问order.OrderItems,EF Core会自动加载已跟踪的关联数据
}

方法2:结合本地实体查询

使用DbSet的Local属性先查找本地已跟踪的实体,找不到再查数据库(适合兼容已存在实体的场景):

var order = _context.Orders.Local
    .FirstOrDefault(o => o.Id == id) 
    ?? await _context.Orders.Include(o => o.OrderItems)
        .SingleOrDefaultAsync(o => o.Id == id, cancellationToken);

方法3:调整事件分发时机(可选)

如果业务允许,可以先保存数据到数据库再分发事件,修改SaveEntitiesAsync的执行顺序:

public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
    // 先提交数据到数据库
    await base.SaveChangesAsync(cancellationToken);

    // 再分发领域事件
    if (_mediator != null)
    {
        await _mediator.DispatchDomainEventsAsync(this);
    }

    return true;
}

这种方式下事件处理器直接查询数据库就能拿到Order,但会变成多事务模式,需要处理最终一致性和失败补偿逻辑,对应原有代码注释中的选项B。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 03:05:18