注入的 Transient 服务仍然在 DbContext 中跟踪 ChangeTracker 中的实体

Injected Transient service still tracks entities in ChangeTracker in DbContext

当我插入一个实体,然后直接尝试更新同一个实体,如下:

var insertedTransferId = await _transferDs.Create_Async(transferObject);

//update transfer key
if (insertedTransferId > 0)
{
    transferObject.TransferKey = $"{transferObject.TransferKey}00{insertedTransferId}";
    return await _transferDs.Update_Async(transferObject, true);
}

_transferDS Create_Async(TransferModel 模型)

public async Task<int> Create_Async(TransferModel model)
        {
            try
            {
                var entity = _mapper.Map<Transfer>(model);

                this._repo.Create<Transfer>(entity);
                this._repo.Context.Entry(entity).Reference(nameof(entity.TransferMeta)).IsModified = false;
                var success = await _repo.SaveAsync() > 0 ? true : false;

                if (success)
                    await this.UpdateTransferByTransferId(entity.TransferId);

                return success ? entity.TransferId : 0;
            }
            catch
            {
                throw;
            }
        }

_repo.Create(实体)

public virtual void Create<TEntity>(TEntity entity)
    where TEntity : class
{
    Context.Set<TEntity>().Add(entity);
}

我运行进入以下问题:

The instance of entity type 'Transfer' cannot be tracked because another instance with the same key value is already being tracked

初始插入后,EntityStateAdded。当我更新实体时,它抱怨实体已经被跟踪。

我想,在 .Net Core DI() 的帮助下,我可以在 TransientLifetime 范围内注入我的上下文、服务等,意思是,无论何时我请求,例如,我的上下文,将提供一个新实例,并且 ChangeTracker 将是干净的。不是这样的。

在下面,您可以看到,我正在设置上下文的生命周期以及服务:

//Database context and repository
services.AddDbContext<IMyContext, MyContext>(builder =>
{
    builder.UseSqlServer(connectionString: Configuration.GetConnectionString("myConnection"), sqlServerOptionsAction:  null);
    builder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
    //builder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
    builder.EnableSensitiveDataLogging(true); 
}, ServiceLifetime.Transient);

services.AddTransient<IRepo, Repo>();

我是不是遗漏了什么,或者不理解什么?

来自docs

Transient

Transient lifetime services (AddTransient) are created each time they're requested from the service container. This lifetime works best for lightweight, stateless services.

所以 Create_AsyncUpdate_Async 方法调用是在 _transferDs 的同一个实例上调用的,该实例将具有相同的数据库上下文实例,导致 _transferDs 需要仅解决 MyContext 一次。

要检查它,您可以尝试将 MyContext 注入 Repo 两次并在 CreateUpdate 上调用不同的(不要在 real/production 代码)。

UPD

如何每次都强制创建一个新实例?

我认为你不应该,你可以检查实体是否已经被跟踪或使用 Find,但如果你坚持 - 你可以注册“工厂”(除了普通注册)并解决它:

services.AddTransient<Func<IRepo>>(s => () => s.GetRequiredService<IRepo>());

Func<IRepo> repoFactory 注入您的 类 并使用它代替 _repo:

var repo = _repoFactory();
repo.Create<Transfer>(entity);  
repo.Context.Entry(entity).Reference(nameof(entity.TransferMeta)).IsModified = false;
var success = await repo.SaveAsync() > 0 ? true : false;