Hi,
I am making a web app where a user can purchase images from an online gallery, the app works as follows:
A user browses the gallery, and adds an image to a cart (I am using angular as the front end) once the user goes to their cart they can check out which upon success sends an order to the API (Asp.net core 2.0+) the order model is quite simple:
public class Order { public int Id { get; set; } public virtual ICollection<ImageFile> Items { get; set; } public string Status { get; set; } public decimal Value { get; set; } public string UserId { get; set; } public ApplicationUser User { get; set; } }
When saving the order with this action in my OrdersController:
[HttpPost] public async Task<IActionResult> PostOrder([FromBody] Order order) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var user = await _context.Users.FindAsync(order.UserId); if (user == null) { ModelState.AddModelError("User", $"A user with ID {order.UserId} could not be found"); return BadRequest(ModelState); } _context.Add(order); await _context.SaveChangesAsync(); return Ok(order); }
The order saves but with no Items, and returns the error:
InvalidOperationException: The instance of entity type 'ImageFile' cannot be tracked because another instance with the key value 'Id:2' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I noticed that if I have just one Item there is no error; however, there are still no items included / saved with the order.
Here is my db context:
//Gallery builder.Entity<Gallery>().HasMany(g => g.Albums).WithOne(g => g.Gallery).OnDelete(DeleteBehavior.Cascade); builder.Entity<Album>().HasMany(a => a.ImageFiles).WithOne(a => a.Album).OnDelete(DeleteBehavior.Cascade); builder.Entity<ImageFile>().HasOne(i => i.Album).WithMany(i => i.ImageFiles); //Orders builder.Entity<Order>().HasOne(o => o.User).WithMany(u => u.Orders); builder.Entity<ApplicationUser>().HasMany(x => x.Orders).WithOne(y => y.User); builder.Entity<Order>().HasMany(o => o.Items).WithOne(); builder.Entity<ImageFile>().HasMany(i => i.Orders).WithOne();
I have had similar problems before and just abandoned the project, but it seems for me to keep using Core 2.0+ I need to learn how to over come this issue.
Any help would be great as this really is going to be the end of me using asp.net core if I can't figure this out, which is a shame given the time I've taken to teach myself.
Thank in advance!
EDIT:
I have made some changes based on: https://blog.oneunicorn.com/2017/09/25/many-to-many-relationships-in-ef-core-2-0-part-1-the-basics/
So I now have a join table named OrdersItems, my Order model no longer has a collection of items, it has a collection of OrdersItems, and I have a OrderViewModel which still has a list of items which will be passed from the front end, and when saving the new order this is my new controller action:
[HttpPost] public async Task<IActionResult> PostOrder([FromBody] OrderVM order) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var user = await _context.Users.FindAsync(order.UserId); if (user == null || user.UserName != un) { ModelState.AddModelError("User", $"A user with ID {order.UserId} could not be found"); return BadRequest(ModelState); } var newOrder = new Order() { //Items = order.Items, Status = "Paid", Value = order.Value, UserId = order.UserId }; foreach (var item in order.Items) { newOrder.OrdersItems.Add(new OrdersItems { Order = newOrder, Item = item }); } _context.Add(newOrder); await _context.SaveChangesAsync(); return Ok(); }
But now I get the following error which is really baffling me, I don't want to touch the Galleries, Albums or anything else, yet it looks like it's trying to add a new copy of things that already exist in the DB!
"SqlException: Violation of PRIMARY KEY constraint 'PK_Galleries'. Cannot insert duplicate key in object 'dbo.Galleries'. The duplicate key value is (ab2cab9a-75fa-48f5-09ef-08d5afb5f113).
The statement has been terminated."