'Optimistic Concurrency Exception
I have been battling with this error for a while now. I hope putting it here will help.
I have attached by models and also the method call being made.
The error occurs when I try to savechanges on creation of a product.
Here is the Method by controller is calling to making the HttpPost request.
//Manager
public async Task<Envelope<ProductViewModel>> CreateProduct(ProductViewModel model)
{
model.ValidateProductModel();
var product = new Product
{
Name = model.Name,
Price = model.Price,
ImageUrl = model.ImageUrl,
Description = model.Description
};
var category = await _categoryRepository.GetCategoryName(model.Category.Name);
category.Products.Add(product);
//_categoryRepository.UpdateEntity(category);
await _categoryRepository.SaveChangesAsync();
//_context.SaveChanges();
ProductViewModel savedProduct = _mapper.Map<Product, ProductViewModel>(product);
return Envelope.Ok(savedProduct);
}
Here is my model classes
//Models
public class Category : DbGuidEntity
{
public string Name { get; set; }
public List<Product> Products { get; set; }
public Category()
{
Products = new List<Product>();
}
public Category(CategoryViewModel model)
:this()
{
model.ValidateCategoryModel();
Name = model.Name;
}
}
public class Product : DbGuidEntity
{
public Product()
{
}
[Required]
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
[ForeignKey("Category")]
public Guid CategoryForeignKey { get; set; }
[JsonIgnore]
public Category Category { get; set; }
public string ImageUrl { get; set; }
}
Then, here is my save changes method and by controller
//SaveChanges Method
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default, bool clearChangeTracker = false)
{
var result = await DbContext.SaveChangesAsync(cancellationToken);
if (clearChangeTracker)
ClearChangeTracker();
return result;
}
//Create Product Controller
[HttpPost("Create")]
[Produces(typeof(Envelope<ProductViewModel>))]
public async Task<IActionResult> Create(ProductViewModel model)
{
try
{
var product = await _productManager.CreateProduct(model);
return Done(product);
}
catch (ShoppingMartException sx)
{
LogError("Error occured while attempting to create product:", sx, model);
return Failure(sx.Errors);
}
catch (Exception ex)
{
LogError("Product Creation error occured", ex);
return Failure($"An error occured | {ex.Message}");
}
}
ErroMessage:
Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions."
Solution 1:[1]
So, I finally found a solution to my question. Basically, EFCore for some reasons wasn't tracking my product entity.
I needed to first check the states of each of the entities inside the createProduct method like so.
_context.Entry(product).Context;
_context.Entry(category).Context;
And I found out that the product entity had a state of Detached (which means untracked), while the category entity has a state of Unchanged.
So, what I did was to manually instruct EFCore to track my product entity like so;
_context.Entry(product).State = EntityState.Added;
This simply put the state of the entity to Added, meaning that it will be inserted into the database on calling savechanges.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | horace willie |