ShoppingCart ;Collection was modified; enumeration operation may not execute - c#

I have a shopping cart list that I want to loop through, when the item is already in the cart the amount has to be increased, otherwise a new object has to be created.
I know you can't change a list during a loop,
so I tried to work with Tolist() but when I try that he only makes the first object and overwrites when I make the second one.
I don't get this fixed, can somebody help me fix it?
[HttpPost, ActionName("Details")]
[ValidateAntiForgeryToken]
public IActionResult DetailsPost(int id, DetailsProductViewModel detailsProductViewModel)
{
List<ShoppingCart> shoppingCartsList = new List<ShoppingCart>();
if (HttpContext.Session.Get<IEnumerable<ShoppingCart>>(WC.SessionCart)!=null
&& HttpContext.Session.Get<IEnumerable<ShoppingCart>>(WC.SessionCart).Count() > 0)
{
shoppingCartsList = HttpContext.Session.Get<List<ShoppingCart>>(WC.SessionCart);
foreach (var item in shoppingCartsList)
{
if (item.ProductId == id)
{
item.Aantal += 1;
}
else
{
shoppingCartsList.Add(new ShoppingCart { ProductId = id, Aantal = detailsProductViewModel.Product.Aantal });
}
}
}
else
{
shoppingCartsList.Add(new ShoppingCart { ProductId = id, Aantal = detailsProductViewModel.Product.Aantal });
}
HttpContext.Session.Set(WC.SessionCart, shoppingCartsList);
return RedirectToAction(nameof(Index));
}

you don't need a loop. just try to find the item in the list. if found, increase amount, if not create new one.
...//also check your shoppingCartsList is null or not
var product = shoppingCartsList.FirstOrDefault(x=>x.ProductId == id);
if(product == null)
{
shoppingCartsList.Add(new ShoppingCart { ProductId = id, Aantal = detailsProductViewModel.Product.Aantal });
}
else
{
product.Aantal++;
}
HttpContext.Session.Set(WC.SessionCart, shoppingCartsList);

Related

CheckOut Cart MVC

I am trying to create a checkout for the cart in my webshop project for classe.
I want that every time that your press checkout it will take from the model Clothes.Amount 1 for each product that you have in the cart session.
and then I want to clean the cart.
In cart controller I have this three functions and one other to order but it is not important for this question i think.
The error is int this lines
Item.Cl.Amount--;
Delete(Item.Cl.Id);
Here is the code
private int isExisting(int id)
{
List<Item> cart = (List<Item>)Session["cart"];
for (int i = 0; i < cart.Count; i++)
{
if (cart[i].Cl.Id == id)
return i;
}return -1;
}
public ActionResult Delete(int idDelete)
{
int index = isExisting(idDelete);
List<Item> cart = (List<Item>)Session["cart"];
cart.RemoveAt(index);
Session["cart"] = cart;
return View("Order");
}
public ActionResult CheckOut(int idCheck)
{
int index = isExisting(idCheck);
if(index != -1) {
foreach (Item item in (List<Item>)Session["cart"]) {
Item.Cl.Amount--;
Delete(Item.Cl.Id);
}
}
return View();
}
From your question I am unable to see which error you are getting. But just looking at your code I would think that this is the classic "Collection was modified; enumeration operation may not execute".
In method CheckOut You are iterating over the item list in your session. While you're doing so you call the Delete method which removes an item from the list. That is simply not allowed...
If that is your issue you could change your second method to the following:
public ActionResult CheckOut(int idCheck)
{
int index = isExisting(idCheck);
if(index != -1) {
foreach (Item item in ((List<Item>)Session["cart"]).ToList()) {
Item.Cl.Amount--;
Delete(Item.Cl.Id);
}
}
return View();
}

not all code paths return an value error for a timer

Orders controller
public async Task<ActionResult> FirstClassCreate(FormCollection values)
{
var order = new Order();
TryUpdateModel(order);
var customer = db.Users.FirstOrDefault(x => x.Email == User.Identity.Name);
var cart = ShoppingCart.GetCart(this.HttpContext);
try
{
//I need to get the order.timeleft to start a count down
order.timeleft = timer();
//Save Order
storeDB.Orders.Add(order);
await storeDB.SaveChangesAsync();
//Process the order
cart = ShoppingCart.GetCart(this.HttpContext);
order.Total = cart.GetFirstClass();
order = cart.CreateOrder(order);
return RedirectToAction("FirstClass", "Checkouts");
}
catch
{
//Invalid - redisplay with errors
return View(order);
}
}
I need the timer to decrease by 1 every second and one the time = 0 return timerleft as false, however I am getting problems with the public ActionResult timer highlighting timer states that '.timer(Order)': not all code paths return a value
public ActionResult timer(Order order)
{
int time;
order.timeleft = true;
time = 30;
do
{
if (time > 0)
{
time = time - 1;
}
else
{
// If the user ran out of time, stop the timer, set timeleft = false
var orderToBeChanged = db.Orders
.Where(o => o.HasPaid == false && o.Email == User.Identity.Name)
.OrderByDescending((o => o.OrderDate)).First();
order.timeleft = false;
db.SaveChanges();
}
} while (time > 0);
}
A method of type ActionResult must return an ActionResult, i.e.
public ActionResult Timer(Order order)
{
...
return myActionResult;
}
Alternatively, if you don't want it to return anything mark it as void
public void Timer(Order order)
{
...
}
In function public ActionResult timer(Order order) in any place you dont return any value, and in header you said this function returns type ActionResult
In your case you can modify timer function in this way:
public bool timer(Order order)
{
...
} while (time > 0);
return order.timeleft;
}

Remove duplicate rows mvc

I have this method
Meeting is a class
Attendees is an ICollection in Meeting
Class
public partial class Meeting
{
public Meeting()
{
this.Attendees = new List<Attendees>();
}
public virtual ICollection<Attendees> Attendees{ get; set; }
[...]
Method Controller
private void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
foreach (var item in model.Attendees.GroupBy(x => x.UserName).Select(y => y.Last()))
{
context.Attendees.Remove(item);
}
}
}
The objective is remove duplicate Attendees with the same username in the table.
But the current method it deletes all records and keeps the duplicate
Where am I going wrong?
Correct version of your method will look like this:
private static void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
var duplicates = new List<Attendees>();
foreach (var item in model.Attendees.GroupBy(x => x.UserName).Where(x=>x.Count()>1))
{
duplicates.AddRange(item.Skip(1));
}
duplicates.ForEach(x=>context.Attendees.Remove(x));
}
}
You can try writing raw SQL and invoking via EF and return Attendees objects in a list.
var query = "Select * from Attendees group by username";
var attendeesList = dbContext.Database.SqlQuery<Attendees>(query).ToList<Attendees>();
As I can see you grouped elements by name and remove last item. So you remove unique elements.
Like this
private void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
var temporaryAtendees = new List<Attendees>();
foreach(var item in model.Attendees)
{
if (temporaryAtendees.Contains(item))
{
context.Attendees.Remove(item);
}
else
{
temporaryAtendees.Add(item);
}
}
}
}

Get the largest Value from table unless null

I'm quite new to C# and ASP.NET (and programming in general) and try to do some simple exercises.
What I am trying to do:
I would like to build a simple MVC App where records will have versions.
That is: Given a record, that I am about to change via the "Edit"-View, this record will not be overwritten. Instead a new record will be created (like a new Version). Both, the old and new record, have the same ItemId (which is not the primary key!), that links them together "semantically". In order to know, which record is the newer Version, the newer record has a VersionId that is +1 the VersionId of the older one.
Currently: I've started working on the Create-Action. A new record shall get a value of 1 for it's VersionId and for ItemId the largest ItemId already in the DB plus 1 - unless there is no record in the DB in which case ItemId shall be 1.
The Model:
namespace HowToUpdate.Models
{
public class ItemWithVersion
{
public int Id { get; set; }
public int ItemNr { get; set; }
public int VersionNr { get; set; }
public string Name { get; set; }
}
}
The Controller Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name")] ItemWithVersion itemWithVersion)
{
if (ModelState.IsValid)
{
// set the ItemNr
int currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr);
itemWithVersion.ItemNr = currentMaxItemNr + 1;
// set the VersionNr
itemWithVersion.VersionNr = 1;
db.ItemWithVersions.Add(itemWithVersion);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(itemWithVersion);
}
Problem: When I run localhost/ItemWithVersion/Create, enter a Value for the Name and Submit, i get the following error:
"The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
Source error: int currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr);"
I tried:
// set the ItemNr
int currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr);
if (currentMaxItemNr == null)
{
itemWithVersion.ItemNr = 1;
}
else
{
itemWithVersion.ItemNr = currentMaxItemNr + 1;
}
Now the error seems to be int currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr);
Also int? currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr); and var currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr); won't do any good.
It's probably basic but I need your help! :) Thx.
Your if-statement is wrong:
if (currentMaxItemNr != null)
currently checks if currentMaxItemNr has a value and if it does, make it 1
So your statement should be if (currentMaxItemNr == null)
Edit:
I can't replicate your error unfortunately, but I did check and found out that there's an exception thrown when calling Max() on an empty List. So it would be better to first call if (db.ItemWithVersions.Count() > 0)
That way you are sure that Max() will return a result. If it fails that statement, you can set currentMaxItemNr to 0
You need to make sure that your table is not empty before calling the Max() method. You can use the Any() method to do that.
int currentMaxItemNr = 0;
if (db.ItemWithVersions.Any())
{
currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr);
}
itemWithVersion.ItemNr = currentMaxItemNr + 1;
// set the VersionNr
itemWithVersion.VersionNr = 1;
db.ItemWithVersions.Add(itemWithVersion);
db.SaveChanges();
Probably, the reason is that Id is declared as a int (not nullable, so null can not be assigned to id) . Try following.
public int? Id { get; set; }
Care to try?
int currentMaxItemNr = db.ItemWithVersions.Max(i => i.ItemNr ?? 1);
This will return currentMaxItemNr = 1 if your i.ItemNr is null.
here is how I would do this. When you click on Edit we run:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name")] ItemWithVersion itemWithVersion)
{
if (ModelState.IsValid)
{
// get the item with highest version
ItemWithVersion item = db.ItemWithVersions.Where(i =>i.ItemNr == itemWithVersion.ItemNr).OrderByDescending(i => i.VersionNr).FirstOrDefault();
//if item doesnt exist we need to create
if(item == null) {
//get the last item with highest ItemNr
ItemWithVersion lastitem = db.ItemWithVersions.OrderByDescending(i => i.ItemNr).FirstOrDefault();
if(lastitem == null) {
//if we didnt find a item, it means is the first item in the DB
itemWithVersion.ItemNr = 1;
} else {
//increment the itemNr for the new Item
itemWithVersion.ItemNr = lastitem.ItemNr + 1;
}
//set version to 1 since is the first version for this new ItemNr
itemWithVersion.VersionNr = 1;
} else {
//if we found a item for the current ItemNr we increase the version for the new item
itemWithVersion.VersionNr = item.VersionNr + 1;
}
db.ItemWithVersions.Add(itemWithVersion);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(itemWithVersion);
}

how to write a recursive function for delete nested folder

I have got a task to delete the hierarchy of folders. When i am deleting a folder, the respective sub folders should be deleted.
public ActionResult DeleteLabel(int id)
{
var query = dbPanAgroDMSContext.LabelMaster.Where(x => x.ParentLabelId == id).ToList();
foreach(var item in query)
{
var query1 = dbPanAgroDMSContext.LabelMaster.Where(x => x.ParentLabelId == item.LabelId).ToList();
dbPanAgroDMSContext.LabelMaster.Remove(item);
foreach (var i in query1)
{
dbPanAgroDMSContext.LabelMaster.Remove(i);
}
}
LabelMaster label = dbPanAgroDMSContext.LabelMaster.Find(id);
dbPanAgroDMSContext.LabelMaster.Remove(label);
dbPanAgroDMSContext.SaveChanges();
return Json(new { Result = "OK" });
}
Instead of using repeated for loops I want to do it in a single block of code.Please help me to create a linq query?
Try this
public ActionResult DeleteLabel(int id)
{
Delete(id);
dbPanAgroDMSContext.SaveChanges();
return Json(new { Result = "OK" });
}
private void Delete(int id)
{
//For given id get all child ones first
var query = dbPanAgroDMSContext.LabelMaster.Where(x => x.ParentLabelId == id).ToList();
foreach(var item in query)
{
//for each child ,delet its' childs by calling recursively
Delete(item.Id);
}
LabelMaster label = dbPanAgroDMSContext.LabelMaster.Find(id);
dbPanAgroDMSContext.LabelMaster.Remove(label);
}

Categories