The instance of entity type cannot be tracked issue

My app is using Entity Framework with repository pattern.
There are 3 services. A, B, C. There’s a Repository R for entity_r. All of these are registered as AsTransient. The code looks somewhat like this (simplified for brevity) –

public class A
{
  public async Task GenerateInvoice(int id)
  {
    using var scope = unitOfWork.Begin();
    await B.Invoice(id, scope);
    await C.ChangeStatus(id, scope);
  }
}

public class B
{
  public async Task Invoice(int id, IUnitOfWork scope)
  {
    var r = await R.GetByIdAsync(id);
    //do stuff
  }
}

public class C
{
  public async Task ChangeStatus(int id, IUnitOfWork scope)
  {
    var r = await R.GetByIdAsync(id);
    r.Status = 4; // new status
    await R.UpdatAsync(r);
    await scope.SaveChangesAsync();
  }
}

I am getting the following error.

The instance of entity type ‘entity_r’ cannot be tracked because
another instance with the same key value for {entity_r_id} is
already being tracked.

I have tried getting entity_r with AsNoTracking() in B.Invoice(). Tried registering repository R as AsScoped. But, still getting the same.
If I pass entity_r from A.GenerateInvoice() to B.Invoice() and C.ChangeStatus() without changing anything else, it works.
Is there a way to get around this without passing entity_r or changing the service-repository scopes to AsScoped?

The problem is most likely your implementation of a unit of work. Unit of Work and Transient are not really compatible. The underlying issue is likely that regardless of the Repository and services being Transient, I suspect the Repository is going to the Scope to fetch or access the DbContext, and while that DbContext instance may also be Transient the Unit of Work by definition of its purpose will be relying on a single instance over its lifetime. Without knowing the implementation of the Unit of Work it is likely that untracked entities are being fetched by each service through the repository, but calling “Update” will begin tracking that instance. The only real solution, since changes are not committed to the database with each step is that your repositories cannot blindly fetch untracked entities without considering that a tracked entity might be available.

Assuming your Repository implementation’s GetByIdAsync does something like:

async Task<Invoice> IRepository.GetByIdAsync(int id)
{
    return await _scope.DbContext.Invoices
        .AsNoTracking()
        .FirstOrDefaultAsync(x => x.Id == id);
}

This needs to be revised to something more like:

async Task<Invoice> IRepository.GetByIdAsync(int id)
{
    var invoice = _scope.DbContext.Invoices
        .Local()
        .FirstOrDefaultAsync(x => x.Id == id);
    if (invoice != null)
       return invoice;

    return await _scope.DbContext.Invoices
        .AsNoTracking()
        .FirstOrDefaultAsync(x => x.Id == id);
}

The first call checks the tracking cache to locate any possible tracked references. If found, they are returned. If not then we can fetch a detached entity.

However, I don’t really recommend this overall approach. For read-only operations it is fine to default to .AsNoTracking() queries, but when it comes to write operations you should fetch, and use tracked entity queries to avoid issues like this, and avoid the .Update() method entirely. The big caveat of updating via untracked references is that even with checking the tracking cache to avoid the exception you are seeing, you will only get state of the tracked entity as it was originally loaded. For instance if service B only needed the invoice, but service C wanted to eager load some additional reference navigation properties, service C using .Include() in its “if not found in tracking cache” implementation would not receive those navigation property when B was called first and the DbContext is tracking an instance without them. This issue is avoided by letting EF manage the tracking as it is designed to do.

Additionally, in a method chain like that, whatever is responsible for initiating the Unit of Work should be the one responsible for committing, or rolling it back. I.e.

public async Task GenerateInvoice(int id)
{
    using var scope = unitOfWork.Begin();
    await B.Invoice(id, scope);
    await C.ChangeStatus(id, scope);
    await scope.SaveChangesAsync();
}

Ideally any commit or rollback of “scope” would not be initiated within into Services B or C. The issue with your implementation is that the execution order is now dependent and if you introduce a step beyond c.ChangeStatus then the SaveChanges must be moved. If you have rolled your own Unit of Work, then the contract interface for the “scope” that is passed to the consumers of the UoW should not expose “SaveChanges”. That either should remain in the UoW implementation:

public async Task GenerateInvoice(int id)
{
    using var scope = unitOfWork.Begin();
    await B.Invoice(id, scope);
    await C.ChangeStatus(id, scope);
    await unitOfWork.SaveChangesAsync(scope);
}

… or the scope implement two interfaces, the IUnitOfWork and a IUnitOfWorkScope where B & C only receive the “scope” as IUnitOfWorkScope which does not expose SaveChanges.

public async Task GenerateInvoice(int id)
{
    using IUnitOfWork scope = unitOfWork.Begin();
    await B.Invoice(id, scope); // accepted as IUnitOfWorkScope so neither can "commit" it, only here after done by who initiated it.
    await C.ChangeStatus(id, scope);
    await scope.SaveChangesAsync();
}

public class B
{
  public async Task Invoice(int id, IUnitOfWorkScope scope)
  {
    //...
  }
}

public class C
{
  public async Task ChangeStatus(int id, IUnitOfWorkScope scope)
  {
      // ...
  }
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật