I just started with asp.net core MVC by developing a Car Rental application and I'm using EF Core for working with the database. The problems comes when I'm trying to update a record from database. It gives me the folloing exception:
InvalidOperationException: The instance of entity type 'Car' cannot be tracked because another instance with the same key value for {'CarID'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Here's my code for updating a car:
Repository:
public Car GetCarByID(int carId)
{
return _context.Cars.Where(c=> c.CarID == carId).Include(c=>c.Category)
.Include(c=>c.Type)
.Include(c=>c.CarLocations).ThenInclude(c=>c.Location)
.FirstOrDefault();
}
public void UpdateCar(Car car)
{
_context.Update(car);
}
Controller car update code:
[HttpGet]
public async Task<IActionResult> Edit(int id)
{
await SeedCarView();
var carToEdit = _carsRepo.GetCarByID(id);
if (carToEdit == null)
{
return NotFound();
}
return View(carToEdit);
}
[HttpPost]
public async Task<IActionResult> Edit(Car car)
{
await SeedCarView();
if (ModelState.IsValid)
{
/*get selections from dropdown form*/
string categSelection = Request.Form["Category_Name"].ToString();
string bodySelection = Request.Form["TypeName"].ToString();
var selectedLocations = Request.Form["AreChecked"].ToList();
var selectedCategory = await _categoriesRepo.GetCategoryByIDAsync(categSelection);
var selectedBodyType = await _typesRepo.GetBodyTypeByNameAsync(bodySelection);
car.Category = selectedCategory;
/*Increment the number of cars in this category*/
car.Category.NumberOfCars = car.Category.Cars.Count + 1;
car.Type = selectedBodyType;
foreach (var sel in selectedLocations)
{
CarLocation carLocation = new CarLocation
{
Car = car
};
var location = _locationsRepo.GetLocationByID(Int32.Parse(sel));
carLocation.Location = location;
car.CarLocations.Add(carLocation);
}
_carsRepo.UpdateCar(car);
await _carsRepo.SaveAsync();
return RedirectToAction("Listing");
}
return View(car);
}
private async Task SeedCarView()
{
var categories = await _categoriesRepo.GetCategoriesAsync();
var bodyTypes = await _typesRepo.GetBodyTypesAsync();
var locations = await _locationsRepo.GetLocationsAsync();
List<string> categorieNames = new List<string>();
List<string> typeNames = new List<string>();
/*get the existing categories name and body types name*/
foreach (var item in categories)
categorieNames.Add(item.Category_Name);
foreach (var item in bodyTypes)
typeNames.Add(item.Name);
ViewBag.Category_Names = categorieNames;
ViewBag.TypeNames = typeNames;
ViewBag.LocationNames = locations;
}
Here's the Service Configuration methode:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<CarRentalContext>(item => item.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ICarsRepository, CarsRepository>();
services.AddScoped<ICarCategoryRepository, CarCategoryRepository>();
services.AddScoped<ICarTypeRepository, CarTypeRepository>();
services.AddScoped<ILocationsRepository, LocationsRepository>();
}
Please help me to fix this issue. I'm stuck at this for more than 2 days and I don't understand where's the problem.
Update:
I've changed my Update methode in Repository with the following code:
public void UpdateCar(Car car)
{
var existingCar = GetCarByID(car.CarID);
_context.Entry(existingCar).CurrentValues.SetValues(car);
//_context.Update(car);
}
and the code in the POST action:
foreach (var sel in selectedLocations)
{
var location = _locationsRepo.GetLocationByID(Int32.Parse(sel));
CarLocation carLocation = new CarLocation
{
CarID = car.CarID,
LocationID = location.Location_ID,
Car = car,
Location = location
};
car.CarLocations.Add(carLocation);
}
but now only the car instance is updated, without childs like CarCategory, Locations and CarType.