1
votes

I've already been looking for similar questions but they didn't help me much. It seems to me that my DbContext needs to be Scoped in order to prevent this. Even though I implemented 'AddScoped' previously, it still did not work.

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
    {
        options.EnableSensitiveDataLogging();
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));

    }, ServiceLifetime.Scoped);

    services.AddDefaultIdentity<IdentityUser>()
        .AddDefaultUI(UIFramework.Bootstrap4)
         AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<IdentityOptions>(options =>
    {
     ...
    });    
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddScoped<ISmartyService, SmartyService>();
}

DbContext:

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
{
}

public DbSet<Brand> Brand { get; set; }
public DbSet<Car> Car { get; set; }
public DbSet<Customer> Customer { get; set; }
public DbSet<Expense> Expense { get; set; }
public DbSet<Expensetype> Expensetype { get; set; }

Tables

Car

public Guid Id { get; set; }

[ForeignKey("Customer")]
public Guid? Customer_Id { get; set; }

[ForeignKey("Brand")]
public Guid Brand_Id { get; set; }

public string Name { get; set; }

public int Purchase_Price { get; set; }

public int Selling_Price { get; set; }

public int Mileage { get; set; }        

public DateTime InCirculationSince { get; set; }

public DateTime Purchased_At { get; set; }

public DateTime Sold_At { get; set; }

public byte[] Picture { get; set; }

public Customer Customer { get; set; }

public Brand Brand { get; set; }

Customer

public Guid Id { get; set; }

[Required]
public string Firstname { get; set; }

[Required]
public string Lastname { get; set; }

public string Address { get; set; }

[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }

[DataType(DataType.PhoneNumber)]
public string Telephone { get; set; }

Brand

public Guid Id { get; set; }

public string Brandname { get; set; }

The other tables are not necessary atm. I didn't have the chance to work on them. I can't really tell what I'm doing wrong. I believe that the logic I implemented for the upload is correct. It could be that this it the part that I'm doing wrong. for the Upload I'm using the following code:

public void AddOrUpdateCar(Car car)
{
    if (car.Id == Guid.Empty)
    {
        dbContext.Car.Add(car);
    }
    else
    {
        //Where the error occurs
        dbContext.Car.Update(car);
    }

    dbContext.SaveChanges();
}

In Controller:

        Car car = null;

        //Update Condition
        if (viewModel.Id != Guid.Empty)
        {
            car = service.GetCar(viewModel.Id);

            car.Name = service.GetBrandAsString(viewModel.Brand) + " " + viewModel.Name;
            car.Brand_Id = viewModel.Brand;
            car.Purchase_Price = viewModel.Purchase_Price;
            car.Mileage = viewModel.Mileage;
            car.InCirculationSince = viewModel.InCirculationSince;
            car.Purchased_At = viewModel.Purchased_At;

            if (viewModel.Customer != null)
            {
                car.Customer_Id = viewModel.Customer.Id;

                //Applying changes to customer
                car.Customer.Firstname = viewModel.Customer.Firstname;
                car.Customer.Lastname = viewModel.Customer.Lastname;
                car.Customer.Address = viewModel.Customer.Address;
                car.Customer.Email = viewModel.Customer.Email;
                car.Customer.Telephone = viewModel.Customer.Telephone;
            }
        }
        //Add condition
        else
        {
            car = new Car()
            {
                Name = service.GetBrandAsString(viewModel.Brand) + " " + viewModel.Name,
                Brand_Id = viewModel.Brand,
                Purchase_Price = viewModel.Purchase_Price,
                Mileage = viewModel.Mileage,
                InCirculationSince = viewModel.InCirculationSince,
                Purchased_At = viewModel.Purchased_At                  
            };
        }

        if (viewModel.Picture != null)
        {
            car.Picture = ResizeImage(viewModel.Picture, 285, 144);
        }

        service.AddOrUpdateCar(car);

Full error message

System.InvalidOperationException: 'The instance of entity type 'Customer' cannot be tracked because another instance with the key value '{Id: 626c03d3-4d66-44db-acad-1342dd9bc4ae}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.'

Any help will be appreciated! Any help that makes this problem less excruciating

2
I saw this error one time when I was trying to update en entity that was get via another dbContext. In your case I'm suspecting the "service" to be the culprit. Where is it added as a scoped service ? - Christian Kouamé
It's not clear which line of code you provided results in the error in your title. Also, provide the full error message. - Jasen

2 Answers

0
votes

In your controller code, you are creating a new instance of a Car entity in order to update it (I'm assuming... because you are using viewModel.Id as the Id).

Because you are creating this instance, instead of retrieving it from the context, it is not tracked by the context and you will get this error when you try to update, because the Id of your newly created Car instance matches one that the context is already tracking.

In order to update an existing entity, you need to retrieve it from the context first, then change it's properties as required, then Update it...

var car = dbContext.Car.Single(x => x.Id == viewModel.Id);

car.Name = "New Name";

dbContext.Car.Update(car);
0
votes

After a lot of trying, I was finally able to solve the problem. Even though I previously updated the customer like this:

car.Customer = viewModel.Customer;

It didn't apply the changes. I believe that EF Core thought that nothing has changed and that I'm trying to add a new item with the same Id. So after telling EF Core that the customer has changed by applying the changes, it worked perfectly.

I changed the code in my question in order to explain my answer. I had to change the code in the controller.