Click here to Skip to main content
15,868,016 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have three class GdsReceptionBlackProduct, Employee and Customer

C#
public partial class GdsReceptionBlackProduct
    {
        public int Id { get; set; }
        public string? CreatedBy { get; set; }
        public virtual Employee? CreatedByNavigation { get; set; }
    }
    
    public void Configure(EntityTypeBuilder<GdsReceptionBlackProduct> entity)
    {
        entity.HasOne(d => d.CreatedByNavigation).WithMany(p => p.CreatedBy)
        .HasPrincipalKey(p => p.IdEmployee)
        .HasForeignKey(d => d.CreatedBy)
        .HasConstraintName("GDSReceptionBlackProduct_Employee_CreatedBy");
        entity.Navigation(e => e.CreatedByNavigation).AutoInclude();
    }  

    public partial class Employee
    {
        public string IdEmployee { get; set; } = null!;
        public int? FkCountry { get; set; }
        public virtual Country Country { get; set; }
        public virtual ICollection<GdsReceptionBlackProduct> CreatedBy { get; } = new List<GdsReceptionBlackProduct>();
    }
    public void Configure(EntityTypeBuilder<Employee> entity)
    {
        entity.HasOne(d => d.Country)
            .WithMany(p => p.Employees)
            .HasForeignKey(d => d.FkCountry)
            .HasConstraintName("FK_Employee_Country");
        entity.Navigation(e => e.Country).AutoInclude();
    }
    public partial class Customer
    {
        public int? Fkcountry { get; set; }
        public virtual Country? Country { get; set; }
        public virtual ICollection<GdsReceptionBlackProduct> GdsReceptionBlackProduct { get; } = new List<GdsReceptionBlackProduct>();
    }
    public void Configure(EntityTypeBuilder<Customer> entity)
    {
        entity.Navigation(e => e.Country).AutoInclude();
    }

I am using unit of work with repository pattern and DI so in FrmGDSReceptionBlackProduct I use it like this

C#
public FrmGDSReceptionBlackProduct(GdsReceptionBlackProductService gdsReceptionBlackProductService,
                                           CustomerService customerService)
    {
        InitializeComponent();
        this.gdsReceptionBlackProductService = gdsReceptionBlackProductService;
        this.customerService = customerService;
    }

When I try to remove GdsReceptionBlackProduct
C#
await gdsReceptionBlackProductService.RemoveGdsReceptionBlackProductAsync(Convert.ToInt32(txtID.Text));

this is the constructor of my generic repository

C#
public class GenericRepository<T> : IGenericRepository<T>, IDisposable where T : class
    
    {
        protected SIMContext _context;
        internal DbSet<T> dbSet;
        public GenericRepository(SIMContext context)
        {
            _context = context;
            _context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
            dbSet = context. Set<T>();
        }
    public virtual async Task<bool> RemoveAsync(object id)
    {
        T? exist = await dbSet.FindAsync(id);
        if (exist == null) throw new ArgumentNullException($"{nameof(RemoveAsync)} entity must not be null");
        dbSet.Remove(exist);
        return true;
    }
}

I get this error message

id could not be removed: The instance of entity type 'Country' cannot
be tracked because another instance with the key value '{IdCountry:1}'
is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.


I am using AutoInclude to show the properties names in grid view.
All CRUD operations are in the same form.
How can I fix this issue?

What I have tried:

when I comment the below line of code
C#
entity. Navigation(e => e.CreatedByNavigation).AutoInclude();

and I trying to remove the GdsReceptionBlackProduct entity it removed successfully.
Posted
Updated 8-Jun-23 22:58pm
Comments
[no name] 8-Jun-23 10:57am    
It's code like this that gives EF a bad rep. I'd suggest "database first" in your case: you can't build in code what you can't prototype using DDL and DML.

1 solution

You have a combination of two settings which are causing this:
C#
entity.Navigation(e => e.Country).AutoInclude();
This causes the Country navigation property to always be included in any query that loads a Customer.
C#
_context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
This causes every query that uses your context to use non-tracking behaviour when loading entities.
Tracking vs. No-Tracking Queries - EF Core | Microsoft Learn[^]

That means your dbSet.FindAsync call loads the Customer and Country entities as disconnected entities.
Disconnected Entities - EF Core | Microsoft Learn[^]

When you pass that entity to Remove, it attaches the entire graph to the context. The Customer entity ends up in the Deleted state. But since the country is not already tracked, it ends up in the Added state.

Since you can't add two countries with the same ID, you get an error at this point.

There are several possible solutions:

1. Remove the AutoInclude for the Country navigation property, and only include it when it's required.

2. Remove the QueryTrackingBehavior.NoTracking setting. (A repository changing global settings on a context it doesn't own is already a code-smell.)

3. Don't load the entity from the database before deleting it:
C#
public virtual async Task<bool> RemoveAsync(T entityToRemove)
{
    try
    {
        var entry = dbSet.Attach(entityToRemove);
        entry.State = EntityState.Deleted;
        await _context.SaveChangesAsync();
        return true;
    }
    catch (DbUpdateConcurrencyException)
    {
        return false;
    }
}
...
await customerRepository.RemoveAsync(new Customer { Id = 42 });

4. Use EF Core 7's new bulk operations[^] to delete the record without loading it:
C#
public async Task<bool> RemoveAsync(Expression<Func<T, bool>> filter)
{
    int recordsAffected = await dbSet.Where(filter).ExecuteDeleteAsync();
    return recordsAffected != 0;
}
...
await customerRepository.RemoveAsync(c => c.Id == 42);
 
Share this answer
 
Comments
Mahfoud Bouabdallah 2022 9-Jun-23 12:52pm    
I greatly appreciate Richard Deeming for providing an excellent lesson on EF Core. It significantly enhanced my understanding of the subject. Specifically, I struggled with the generic repository and unit of work concept, which caused me considerable frustration. Could you kindly offer some guidance on how to adopt a more effective approach?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900