注入的 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
初始插入后,EntityState
为 Added
。当我更新实体时,它抱怨实体已经被跟踪。
我想,在 .Net Core DI() 的帮助下,我可以在 Transient
的 Lifetime
范围内注入我的上下文、服务等,意思是,无论何时我请求,例如,我的上下文,将提供一个新实例,并且 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_Async
和 Update_Async
方法调用是在 _transferDs
的同一个实例上调用的,该实例将具有相同的数据库上下文实例,导致 _transferDs
需要仅解决 MyContext
一次。
要检查它,您可以尝试将 MyContext
注入 Repo
两次并在 Create
和 Update
上调用不同的(不要在 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;
当我插入一个实体,然后直接尝试更新同一个实体,如下:
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
初始插入后,EntityState
为 Added
。当我更新实体时,它抱怨实体已经被跟踪。
我想,在 .Net Core DI() 的帮助下,我可以在 Transient
的 Lifetime
范围内注入我的上下文、服务等,意思是,无论何时我请求,例如,我的上下文,将提供一个新实例,并且 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_Async
和 Update_Async
方法调用是在 _transferDs
的同一个实例上调用的,该实例将具有相同的数据库上下文实例,导致 _transferDs
需要仅解决 MyContext
一次。
要检查它,您可以尝试将 MyContext
注入 Repo
两次并在 Create
和 Update
上调用不同的(不要在 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;