C# List.Add overwrites previous objects - c#

I am trying to create a simple list of objects, but somehow on every foreach loop the previous records are overwritten by the new one loop the previous record is overwritten by the new record. So if there are 6 entries in realData, the list will have 6x the last record.
Do I somehow recreate the List instead of adding to it? Is there another alternative that I have overlooked to create a List?
My code is
public async Task<IActionResult> OrderOverview()
{
var itemList = new List<OrderItemVM>();
var realData = await _context.OrderItem.ToListAsync();
var orderItemVM = new OrderItemVM();
foreach (var item in realData)
{
orderItemVM.Id = item.Id;
orderItemVM.OrderId = item.OrderId;
orderItemVM.OrderName = _context.Order.Find(item.OrderId).OrderName;
orderItemVM.ItemName = item.ItemName;
itemList.Add(orderItemVM);
}
return View(itemList);
}

You are modifying the previously added objects instead of adding a new one. You should do this.
foreach (var item in realData)
{
OrderItemVM orderItemVM = new OrderItemVM ();
orderItemVM.Id = item.Id;
orderItemVM.OrderId = item.OrderId;
orderItemVM.OrderName = _context.Order.Find(item.OrderId).OrderName;
orderItemVM.ItemName = item.ItemName;
itemList.Add(orderItemVM);
}
So, basically on each iteration you create a new empty object and then assign that values and add that in List.

It happens because you are inserting the same reference of orderItemVM to itemList.
Also, you can set a default size for itemList and boost performance.
var realData = await _context.OrderItem.ToListAsync();
var itemList = new List<OrderItemVM>(realData.Count);
And for this task, you can use LINQ:
public async Task<IActionResult> OrderOverview()
{
var realData = await _context.OrderItem.ToListAsync();
var itemList = realData.Select(item => new OrderItemVM
{
Id = item.Id,
OrderId = item.OrderId,
OrderName = _context.Order.Find(item.OrderId).OrderName,
ItemName = item.ItemNam,
}).ToList();
return View(itemList);
}

Thanks to Lasse V. Karlsen I discovered the error. I moved the line var OrderItemVM = new OrderItemVM in the Foreach-loop. That solved it.

Related

Could not find an implementation of the query pattern for source tpe "Task<List<Stocks>> StockList" select not found

I tried to improve my desktop application by Adding Async/Await querying a database here is my code
public async Task<List<Stocks>> StockListAsync()
{
List<Stocks> stocks = new List<Stocks>();
{
conn = await App_Code.DbConnection.InitializeConnectionAsync();
string cnt = "SELECT * FROM tblStock";
cmd = new SqlCommand(cnt, conn);
rd = cmd.ExecuteReader();
if (rd.HasRows == true)
{
while (await rd.ReadAsync())
{
var rm = new Stocks
{
ID = Convert.ToInt32(rd["ID"]),
Supplier = Convert.ToInt32(rd["Supplier"]),
StockCode = rd["StockCode"].ToString(),
StockName = rd["StockName"].ToString(),
Description = rd["Description"].ToString(),
UnitMeasure = rd["Measurement"].ToString(),
Quantity = Convert.ToInt32(rd["Quantity"]),
OrderQty = Convert.ToInt32(rd["OrderQty"]),
Cost = Convert.ToDouble(rd["Cost"]),
};
stocks.Add(rm);
}
}
return stocks;
}
//}
}
This code above is a separate class called Stocks then I the windows form I want to display the result I have created an instance of the stock class like this * readonly Stocks stock = new Stocks();* and I also have a method GetStock below is the code
void GetStocks()
{
var list = stock.StockListAsync();
var result = from g in list
select g;
GrdFood.Rows.Clear();
foreach (var item in result)
{
GrdFood.Rows.Add(GrdFood.RowCount + 1, item.Supplier, item.StockName,
item.Description, item.Quantity.ToString("N2"), item.OrderQty.ToString("N2"),
Convert.ToDouble(item.Cost).ToString("N2"), item.ID);
}
}
Then on the FormLoad event I called GetStocks The problem is that the List in the code below is reporting an error Could not find an implementation of the query pattern for source type Task> StockList select not found
var list = stock.StockListAsync();
var result = from g in list
select g;
Everything was working fine before I made it Async Task, please how do I correct this implementation thanks.
Because you aren't using async/await properly, the type of list is Task<List<Stock>>, not List<Stock> because that's how async works
Task<List<Stock>>list = stock.StockListAsync();
What you actually want is:
List<Stock>> list = await stock.StockListAsync().ConfigureAwait(false);
But, to use await, your function must be async:
async Task GetStocksAsync() <-- Async functions should have an Async suffix
{
var list = await stock.StockListAsync().ConfigureAwait(false);
var result = from g in list
select g;
GrdFood.Rows.Clear();
foreach (var item in result)
{
GrdFood.Rows.Add(GrdFood.RowCount + 1, item.Supplier, item.StockName,
item.Description, item.Quantity.ToString("N2"), item.OrderQty.ToString("N2"),
Convert.ToDouble(item.Cost).ToString("N2"), item.ID);
}
}
Then your problem is that you need to change your code that calls GetStocksAsync to also be async/await etc etc. At this point you are probably wondering if it's all really worth the effort to rewrite something that already works to be async.
Change the method to async and await the call stock.StockListAsync.
Something like this
async void GetStocks()
{
var list = await stock.StockListAsync();
var result = from g in list
select g;
GrdFood.Rows.Clear();
foreach (var item in result)
{
GrdFood.Rows.Add(GrdFood.RowCount + 1, item.Supplier, item.StockName,
item.Description, item.Quantity.ToString("N2"), item.OrderQty.ToString("N2"),
Convert.ToDouble(item.Cost).ToString("N2"), item.ID);
}
}
StockListAsync() is returning Task<List<Stocks>> not List<Stocks> to apply query hence the error.

How to add distinct value in database using Entity Framework

IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
}
}
db.SaveChanges();
I want to add only distinct values to database in above code. Kindly help me how to do it as I am not able to find any solution.
IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
var a = db.WebsiteWebPages.Where(i => i.WebPage == value.WebPage.ToString()).ToList();
if (a.Count == 0)
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
db.SaveChanges();
}
}
}
This is the code that I used to add distinct data.I hope it helps
In addition to the code sample Furkan Öztürk supplied, Make sure your DB has a constraint so that you cannot enter duplicate values in the column. Belt and braces approach.
I assume that by "distinct values" you mean "distinct value.WebPage values":
// get existing values (if you ever need this)
var existingWebPages = db.WebsiteWebPages.Select(v => v.WebPage);
// get your pages
var webPages = GetWebPages().Where(v => v.WebPage.Contains(".htm"));
// get distinct WebPage values except existing ones
var distinctWebPages = webPages.Select(v => v.WebPage).Distinct().Except(existingWebPages);
// create WebsiteWebPage objects
var websiteWebPages = distinctWebPages.Select(v =>
new WebsiteWebPage { WebPage = v, WebsiteId = websiteid});
// save all at once
db.WebsiteWebPages.AddRange(websiteWebPages);
db.SaveChanges();
Assuming that you need them to be unique by WebPage and WebSiteId
IEnumerable<WebsiteWebPage> data = GetWebPages();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
if (db.WebsiteWebPages.All(c=>c.WebPage != value.WebPage|| c.WebsiteId != websiteid))
{
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
db.WebsiteWebPages.Add(pagesinfo);
}
}
}
db.SaveChanges();
UPDATE
To optimize this (given that your table contains much more data than your current list), override your equals in WebsiteWebPage class to define your uniqueness criteria then:
var myWebsiteWebPages = data.select(x=> new WebsiteWebPage { WebPage = x.WebPage, WebsiteId = websiteid}).Distinct();
var duplicates = db.WebsiteWebPages.Where(x=> myWebsiteWebPage.Contains(x));
db.WebsiteWebPages.AddRange(myWebsiteWebPages.Where(x=> !duplicates.Contains(x)));
this is a one database query to retrieve ONLY duplicates and then removing them from the list
You can use the following code,
IEnumerable<WebsiteWebPage> data = GetWebPages();
var templist = new List<WebsiteWebPage>();
foreach (var value in data)
{
if (value.WebPage.Contains(".htm"))
{
WebsiteWebPage pagesinfo = new WebsiteWebPage();
pagesinfo.WebPage = value.WebPage;
pagesinfo.WebsiteId = websiteid;
templist.Add(pagesinfo);
}
}
var distinctList = templist.GroupBy(x => x.WebsiteId).Select(group => group.First()).ToList();
db.WebsiteWebPages.AddRange(distinctList);
db.SaveChanges();
Or you can use MoreLINQ here to filter distinct the list by parameter like,
var res = tempList.Distinct(x=>x.WebsiteId).ToList();
db.WebsiteWebPages.AddRange(res);
db.SaveChanges();

Add items to list from IEnumerable using LinQ

I'm adding new items to a list from a IEnumerable (query.Roles).
var query = GetRoles();
var vm = new CreateUserViewModel();
vm.Role = new List<CreateUserViewModel.Item>();
foreach (var Role in query.Roles)
{
vm.Role.Add(new CreateUserViewModel.Item
{
Label = Role.Label,
RoleNumber = Role.RoleNumer
});
}
How i can do the 'Add' to the list with Linq?
AddRange should do it for you:
vm.Role.AddRange(query.Roles.Select(r => new CreateUserViewModel.Item
{
Label = r.Label,
RoleNumber = r.RoleNumer
}));
AddRange takes an IEnumerable parameter and adds each item to the collection.
vm.Role = query
.Roles
.Select(r=>new CreatUserViewModel
.Item{Label = r.Label,
RoleNumber = r.RoleNumber})
.ToList();

Exclude certain items from being added to a list with C#

I am getting data from a Web Api and adding it to a list in my Windows store touch app with c#. I use the code below which works fine.
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync("http://localhost:12345/api/items");
var info = new List<SampleDataGroup>();
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var item = JsonConvert.DeserializeObject<dynamic>(content);
foreach (var data in item)
{
var infoSect = new info
(
(string)data.Id.ToString(),
(string)data.Name,
(string)"",
(string)data.PhotoUrl,
(string)data.Description
);
info.Add(infoSect);
}
}
else
{
MessageDialog dlg = new MessageDialog("Error");
await dlg.ShowAsync();
}
this.DefaultViewModel["Sections"] = info;
How do I check the data retrieved from the web api, to exclude items from the list whose Name contains the word "Test" in it. For example, an item with the Name "Soda Test" should not be included in the list.
So if Name contains "Test", do not add to list.
var items = item.Where( d =>!d.Name.Contains("Test")).ToList();
items.ForEach(i => info.Add(new info(){
data.Id.ToString(),
...
}));
!you should be able to do this using Linq.
info.AddRange(item.Where(i => !i.Name.Contains("Test"))
.Select(i => {
new info
(
(string)data.Id.ToString(),
(string)data.Name,
(string)"",
(string)data.PhotoUrl,
(string)data.Description
)}));
var item = JsonConvert.DeserializeObject<dynamic>(content).Where(i=>!i.Name.Contains("Test"))
Try this :
if(!data.Name.ToString().Contains("Test"))
{
//Add Item to List
}

How to add a number of records into a List<T>

I have created an asp.net application using Entity Framework. In this I want to add the records into a list. For this I have to use the foreach loop but it always adding only last record data for all records, meaning it's showing same data. Here I have pasted my code. Please verify it once and guide where I can change.
public List<CategoryItems> ListMenuCategory(int MenuId)
{
string str = string.Empty;
string strJSON = string.Empty;
List<CategoryItems> resultmenu;
resultmenu = new List<CategoryItems>();
List<CategoryItems> Result;
Result = new List<CategoryItems>();
bool check = true;
var objmenuCategory = from cat in objEntity.menucategories where cat.MenuId == MenuId && cat.Active == check select cat;
CategoryItems Categorylist = new CategoryItems();
foreach (menucategory category in objmenuCategory)
{
Categorylist.CategoryName = category.CategoryName;
Categorylist.Description = category.Description;
int menuid = category.MenuCategoryId;
List<menuitem> menuitems = GetMenucategories(menuid);
foreach (var items in menuitems)
{
Categorylist.ItemName = items.ItemName;
Categorylist.Description = items.Description;
Categorylist.Price = (float)items.Price;
string Image = items.Picture;
Categorylist.Picture = "http://restaurantmanager.testshell.net/Images/" + Image;
Categorylist.Thumbnail = "http://restaurantmanager.testshell.net/Images/" + items.Thumbnail;
if (items.CreatedDate != null)
{
Categorylist.CreatedDate = (DateTime)items.CreatedDate;
}
if (items.ModifiedDate != null)
{
Categorylist.ModifiedDate = (DateTime)items.ModifiedDate;
}
Result.Add(Categorylist);
}
// Result.AddRange(menus);
}
return Result;
}
private List<menuitem> GetMenucategories(int p)
{
restaurantEntities objEntity1 = new restaurantEntities();
var menuitems = from items in objEntity1.menuitems where items.MenuCategoryId == p select items;
return menuitems.ToList();
}
You are creating the Categorylist item outside of the loops, so you are only using one single item, filling it with different data and adding it over and over to the result.
You have to create the item inside the innermost loop, so that each iteration gets its own object.
Note: ChrisF also spotted that you call AddRange inside the loop, which has the result that you will add the same set of items over and over. You don't need to call AddRange at all, you can just skip the Result list entirely and just return resultmenu instead.

Categories