【问题标题】:Injected Transient service still tracks entities in ChangeTracker in DbContext注入的 Transient 服务仍然在 DbContext 中跟踪 ChangeTracker 中的实体
【发布时间】:2020-09-27 23:19:17
【问题描述】:

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

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);
}

我遇到了以下问题:

无法跟踪实体类型“Transfer”的实例,因为已在跟踪具有相同键值的另一个实例

在初始插入之后,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>();

我是错过了什么,还是不明白什么?

【问题讨论】:

    标签: .net entity-framework asp.net-core entity-framework-core ef-core-3.1


    【解决方案1】:

    来自docs

    瞬态

    每次从服务容器请求时都会创建瞬态生命周期服务 (AddTransient)。这个生命周期最适合轻量级、无状态的服务。

    所以Create_AsyncUpdate_Async 方法调用在_transferDs 的同一个实例上调用,这将具有相同的数据库上下文实例,因为_transferDs 只需要解析MyContext 一次。

    要检查它,您可以尝试将MyContext 注入Repo 两次,然后在CreateUpdate 上调用不同的(尽管不要在实际/生产代码中这样做)。

    UPD

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

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

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

    Func&lt;IRepo&gt; 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;
    

    【讨论】:

    • _transferDS 也添加了“瞬态”的生命周期范围。那么它不会创建一个新的 _transferDS 实例,只要我尝试访问 _transferDS 吗?
    • 不,它只在您的代码中注入一次(我假设您提供的代码是一种方法)
    • 我如何每次都强制一个新实例?
    • 出色的答案。谢谢 :-) 我试图清除 ChangeTracker,但它给出了实体关系被切断的错误。
    • @monstertjie_za 很乐意提供帮助!
    猜你喜欢
    • 1970-01-01
    • 2015-10-20
    • 2012-04-24
    • 1970-01-01
    • 2021-08-05
    • 1970-01-01
    • 2020-01-17
    • 2023-03-08
    • 2011-03-10
    相关资源
    最近更新 更多