linq 即使使用AsNoTracking也无法跟踪实体类型的示例

wmtdaxz3  于 2023-03-10  发布在  其他
关注(0)|答案(3)|浏览(244)

我在测试环境中收到错误,但在生产环境中没有收到错误,即使尚未放置跟踪。
错误“获取用户异步:系统操作无效异常:无法跟踪实体类型“UserEntity”的示例,因为键值为“{Id:已在跟踪“1}”。附加现有实体时,请确保只附加一个具有给定键值的实体示例。
代码:

internal async Task<UserEntity> GetUserAsync(CancellationToken cancellationToken)
{
    var result = await _userDataQuery.GetLatestUser(cancellationToken);
}

我已经添加了AsNoTracking

public async Task<UserEntity> GetLatestUser(CancellationToken cancellationToken)
{
    var result = await this.Entities
                           .OrderByDescending(m => m.Id)
                           .AsNoTracking().FirstOrDefaultAsync();
    await Context.Entry(result).ReloadAsync();

    return result;
}

不确定这是否有帮助,但我还注意到两个环境之间PostgreSQL上的pg_sequence对于用户表是不同的,并且它们在表中都只有1条记录
测试环境:

last_value = 2

生产环境:

last_value = 1
hm2xizp9

hm2xizp91#

此错误意味着在DbContext(上下文)的生存期范围内,该实体已被加载并正在被跟踪。这可能是通过其他方法调用直接实现的,也可能是通过作为另一个实体加载的一部分而被提前或延迟加载而间接实现的。Reload将尝试跟踪该实体,这将导致在已跟踪另一个匹配示例时出错。
根据我对你所做的事情的理解,你可能不想使用AsNoTracking(),因为你不能保证实体没有被跟踪。

var result = await this.Entities
                       .OrderByDescending(m => m.Id)
                       .FirstAsync();
await Context.Entry(result).ReloadAsync();

无论何时使用*OrDefault()风格,您 * 必须 * 处理集合中可能没有项的事实。如果您希望至少有一个项,请使用First
这里的缺点是,即使所需的项目已经从DB中新加载,也会调用Reload。由于我们不能假设所需的项目是否已经被跟踪,所以最好的选择可能是:

var id = this.Entities
             .OrderByDescending(m => m.Id)
             .Select(x => x.Id)
             .FirstAsync();

var result = this.Entities.Local
                       .Where(m => m.Id == id)
                       .SingleOrDefault();
if (result != null)
    await Context.Entry(result).ReloadAsync();
else
    result = this.Entities
        .Where(m => m.Id == id)
        .Single();

这段代码在本地跟踪缓存中查找一个特定的项目,如果找到,它将重新加载它,否则它将从DB加载当前项目。
这种方法(不从DB中选择ID)适用于任何其他情况,即您提前知道需要什么特定实体,而不是依赖于FirstOrDefault之类的东西,并且希望确保任何已跟踪的实体可用并刷新它。

nhhxz33t

nhhxz33t2#

只要检查下面的东西。
1.无论你是在代码的任何地方执行context.Update(还是context.Entity.Update(.(这可能会导致这样的问题)
1.请确保不要将DbContext示例保留太长时间。根据MS的建议,它们应该是Transient或Scoped(而不是singleton)。
如果您在代码中的任何地方执行context.Update(,并将DbContext示例保持相当长的时间,则在DbContext生存期内尝试多次更新同一个被跟踪实体的可能性很大。

cwdobuhd

cwdobuhd3#

ReloadAsync()AsNoTracking()不兼容,因为它意味着使用跟踪的实体,而如果先前应用了AsNoTracking(),则不是这种情况。
如果我们需要刷新数据库中的数据,我们可以使用GetDatabaseValuesAsync()代替,它可以像ReloadAsync()一样工作,但是不想改变实体跟踪器。

相关问题