Stepping out of foreach after finding right productID - c#

Each of our products are based on a certain categoryID
When looping through our products it has to first find the product within that category (which it does) and then step out of it. The problem is that when it has gone through the foreach the first time (finding the right prouducts) it does not step out, it just keeps going through it finding the remainding products with other categoryIDs.
How do we solve this?
(We are using C# - Entity Framework - MSSQL)
CHEERS!
public ActionResult Index(int id)
{
var m = new Models.Product.Index();
//m.DisplayName = "Produkter";
//var scp = db.SCPconnection.FirstOrDefault(s => s.CategoryID == id);
//if (id == scp.ProductID)
{
foreach (var item in db.Product.OrderBy(p => p.ProductName))
{
var mp = new Models.Product.ModelProduct();
mp.SectorName = item.ProductName;
mp.ProductID = item.ProductID;
mp.DetailsUrl = item.Details;
m.AllProducts.Add(mp);
}
}
return View(m);
}

Just break it.
foreach (var item in db.Product.OrderBy(p => p.ProductName))
{
var mp = new Models.Product.ModelProduct();
mp.SectorName = item.ProductName;
mp.ProductID = item.ProductID;
mp.DetailsUrl = item.Details;
m.AllProducts.Add(mp);
if(condition) //if it is needed
break;
}

It is better to filter using Where clause for a particular ID.
I would strongly suggest Optimized code, why would you require to get all Products details to business logic, instead query for what is needed with a condition.
foreach (var item in db.Product.Where(p => p.ProductID.Equals(requiredID)).OrderBy(p => p.ProductName))
{
var mp = new Models.Product.ModelProduct();
mp.SectorName = item.ProductName;
mp.ProductID = item.ProductID;
mp.DetailsUrl = item.Details;
m.AllProducts.Add(mp);
}

The break keyword immediately exits the foreach at that point. Code after the break and remaining iteration items will NOT be processed.
public ActionResult Index(int id)
{
var m = new Models.Product.Index();
foreach (var item in db.Product.OrderBy(p => p.ProductName))
{
var mp = new Models.Product.ModelProduct();
mp.SectorName = item.ProductName;
mp.ProductID = item.ProductID;
mp.DetailsUrl = item.Details;
m.AllProducts.Add(mp);
if (item.ProductID == id)
break;
}
}
return View(m);
}

Related

Change orderby depending on value of var

I have a foreach with an ordebydescending(), but in one case I need to use an orderby() instead. Depending on the value of articleType how can I use an inline condition inside the foreach to allow this to happen.
This is the condition I need to build into to determine the use of orderbydescending or orderby
if (articleType == BusinessLogic.ArticleType.Webinar)
This is the full function
public static List<Article> GetArticles(BusinessLogic.ArticleType articleType, long languageID)
{
List<Article> articles = new List<Article>();
using (var db = new DatabaseConnection())
{
foreach (var record in db
.GetArticles(BusinessLogic.SystemComponentID, (int) articleType, languageID)
.OrderByDescending(c => c.Date_Time))
{
#region articleTextLanguageID
long articleTextLanguageID = GetArticleTextLanguageID(record.ID, languageID);
string previewImageName = GetArticle(record.ID, articleTextLanguageID).PreviewImageName;
#endregion
Article article = new Article()
{
ID = record.ID,
Title = record.Title,
Summary = record.Summary,
PreviewImageName = previewImageName,
Date = record.Date_Time,
ArticleTextLanguageID = articleTextLanguageID
};
articles.Add(article);
}
}
return articles;
}
Was thinking something along these lines, but its not working
foreach (var record in db
.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID)
.Where(articleType == BusinessLogic.ArticleType.Webinar.ToString()?.OrderByDescending(c => c.Date_Time)) : .OrderBy(c => c.Date_Time)))
The way to do this would be to construct the query in pieces. For example:
var query = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = query.OrderByDescending(c => c.Date_Time);
}
else
{
query = query.OrderBy(c => c.Date_Time);
}
foreach(var record in query)
{
// process
}
If you required additional sorting, you'd need an extra variable typed as IOrderedEnumerable/IOrderedQueryable (depending on what GetArticles returns) as an intermediate to chain ThenBy/ThemByDescending:
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
IOrderedEnumerable<Article> query;
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = source.OrderByDescending(c => c.Date_Time);
}
else
{
query = source.OrderBy(c => c.Date_Time);
}
if(somethingElse)
{
query = query.ThenBy(c => c.OtherProperty);
}
foreach(var record in query)
{
// process
}
Based on your comment below, as notes above the second example would look more like the following (this means that db.GetArticles returns an IQueryable<Article> and not an IEnumerable<Article>):
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
IOrderedQueryable<Article> query;
if(articleType == BusinessLogic.ArticleType.Webinar)
{
query = source.OrderByDescending(c => c.Date_Time);
}
else
{
query = source.OrderBy(c => c.Date_Time);
}
if(somethingElse)
query = query.ThenBy(c => c.OtherProperty);
foreach(var record in query)
{
// process
}
You could also shorten it to the following:
var source = db.GetArticles(BusinessLogic.SystemComponentID, (int)articleType, languageID);
var query = articleType == BusinessLogic.ArticleType.Webinar
? source.OrderByDescending(c => c.Date_Time)
: source.OrderBy(c => c.Date_Time);
if(somethingElse)
query = query.ThenBy(c => c.OtherProperty);
foreach(var record in query)
{
// process
}

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();

C# Entity Framework Bulk Update

What is the best way to update multiple records in a list to speed up processing?
Currently, I'm updating about 15000 products, each with 3 different price sets and it takes the whole day to complete.
I need to update the prices all at once in code side, then commit those changes to the database in 1 go, instead of fetching each inventory item, updating its values, then attaching it to the context. Every single fetch is causing the delays.
Code
public void UpdatePricesFromInventoryList(IList<Domain.Tables.Inventory> invList)
{
var db = new UniStockContext();
foreach (var inventory in invList)
{
Domain.Tables.Inventory _inventory = db.Inventories
.Where(x => x.InventoryID == inventory.InventoryID)
.FirstOrDefault();
if (inventory.Cost.HasValue)
_inventory.Cost = inventory.Cost.Value;
else
_inventory.Cost = 0;
foreach (var inventoryPrices in inventory.AccInventoryPrices)
{
foreach (var _inventoryPrices in _inventory.AccInventoryPrices)
{
if (_inventoryPrices.AccInventoryPriceID == inventoryPrices.AccInventoryPriceID)
{
_inventoryPrices.ApplyDiscount = inventoryPrices.ApplyDiscount;
_inventoryPrices.ApplyMarkup = inventoryPrices.ApplyMarkup;
if (inventoryPrices.Price.HasValue)
_inventoryPrices.Price = inventoryPrices.Price.Value;
else
_inventoryPrices.Price = _inventory.Cost;
if (inventoryPrices.OldPrice.HasValue)
{
_inventoryPrices.OldPrice = inventoryPrices.OldPrice;
}
}
}
}
db.Inventories.Attach(_inventory);
db.Entry(_inventory).State = System.Data.Entity.EntityState.Modified;
}
db.SaveChanges();
db.Dispose();
}
I've also tried working my code according to this SOQ Entity Framework update/insert multiple entities
and it gave me and error. Here are the details:
Code:
public void UpdatePricesFromInventoryListBulk(IList<Domain.Tables.Inventory> invList)
{
var accounts = new List<Domain.Tables.Inventory>();
var db = new UniStockContext();
db.Configuration.AutoDetectChangesEnabled = false;
foreach (var inventory in invList)
{
accounts.Add(inventory);
if (accounts.Count % 1000 == 0)
{
db.Set<Domain.Tables.Inventory>().AddRange(accounts);
accounts = new List<Domain.Tables.Inventory>();
db.ChangeTracker.DetectChanges();
db.SaveChanges();
db.Dispose();
db = new UniStockContext();
}
}
db.Set<Domain.Tables.Inventory>().AddRange(accounts);
db.ChangeTracker.DetectChanges();
db.SaveChanges();
db.Dispose();
}
Error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I would suggest changing the following:
Domain.Tables.Inventory _inventory = db.Inventories
.Where(x => x.InventoryID == inventory.InventoryID)
.FirstOrDefault();
To
Domain.Tables.Inventory _inventory = db.Inventories
.Single(x => x.InventoryID == inventory.InventoryID);
I'd still add the db.Configuration.AutoDetectChangesEnabled = false; after getting the context, and also use AsNoTracking:
Turn off EF change tracking for any instance of the context
that is because you are hit the database context at every loop to increase the performance you should get all the Inventories by one hit ,this is your problem try the below code and you will notice the performance :
public void UpdatePricesFromInventoryList(IList<Domain.Tables.Inventory> invList)
{
var db = new UniStockContext();
invIdsArray = invList.select(x => x.InventoryID).ToArray();
IList<Domain.Tables.Inventory> invListFromDbByOneHit = db.Inventories.Where(x => invIdsArray.Contains(x.InventoryID)).Tolist();
foreach (var inventory in invListFromDbByOneHit)
{
//Domain.Tables.Inventory _inventory = db.Inventories
//.Where(x => x.InventoryID == inventory.InventoryID)
//.FirstOrDefault();
if (inventory.Cost.HasValue)
_inventory.Cost = inventory.Cost.Value;
else
_inventory.Cost = 0;
foreach (var inventoryPrices in inventory.AccInventoryPrices)
{
foreach (var _inventoryPrices in _inventory.AccInventoryPrices)
{
if (_inventoryPrices.AccInventoryPriceID == inventoryPrices.AccInventoryPriceID)
{
_inventoryPrices.ApplyDiscount = inventoryPrices.ApplyDiscount;
_inventoryPrices.ApplyMarkup = inventoryPrices.ApplyMarkup;
if (inventoryPrices.Price.HasValue)
_inventoryPrices.Price = inventoryPrices.Price.Value;
else
_inventoryPrices.Price = _inventory.Cost;
if (inventoryPrices.OldPrice.HasValue)
{
_inventoryPrices.OldPrice = inventoryPrices.OldPrice;
}
}
}
}
db.Inventories.Attach(_inventory);
db.Entry(_inventory).State = System.Data.Entity.EntityState.Modified;
}
db.SaveChanges();
db.Dispose();
}

LINQ Filtering Select Ouput with IEnumerable<T>

I have following methods:
Controller:
...
var appmap = Services.GetReqAppMapList(value);
var applist = Services.GetApplicationList(docid, appid, reqid, appmap);
...
Model:
public static IEnumerable<AppMap> GetReqAppMapList(int aiRequestTypeId)
{
try
{
var appmap = new List<AppMap>();
using (var eties = new eRequestsEntities())
{
appmap = (from ram in eties.ReqAppMaps
where ram.IsActive == 1
select new AppMap
{
RequestTypeId = ram.RequestTypeId
}).ToList();
return appmap;
}
}
catch(Exception e)
{
throw e;
}
}
public static IEnumerable<TicketApplication> GetApplicationList(int aiDocumentTypeId, int aiApplicationTypeId, int aiRequestTypeId, IEnumerable<AppMap> appmap)
{
try
{
var applicationlist = new List<TicketApplication>();
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
where 1==1
<<<Some Conditions Here???>>>
== && appmap.Contains(app.ApplicationTypeId) ==
&& app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId = app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
return applicationlist;
}
And I was thinking how can filter query result of GetApplicationList using the result from GetReqAppMapList
I'm kinda stuck with the fact that I must convert/cast something to the correct type because every time I do a result.Contains (appmap.Contains to be exact), I always get the following error
Error 4 Instance argument: cannot convert from
'System.Collections.Generic.IEnumerable<Test.Models.AppMap>' to
'System.Linq.ParallelQuery<int?>'
You should directly join the two tables in one query.
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
join ram in applicationentity.ReqAppMaps on app.ApplicationTypeId equals ram.RequestTypeId
where ram.IsActive == 1 && app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId = app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
You can delete the other method if it is not needed anymore. No point hanging onto code which is dead.
Looks like there is no other way to do this (as far as I know), so I have to refactor the code, I hope still that there would be a straight forward conversion and matching method in the future (too lazy). Anyway, please see below for my solution. Hope this helps someone with the same problem in the future. I'm not sure about the performance, but this should work for now.
Controller:
...
var appmap = Services.GetReqAppMapList(value);
var applist = Services.GetApplicationList(docid, appid, reqid, appmap);
...
Model:
<Removed GetReqAppMapList>--bad idea
public static IEnumerable<TicketApplication> GetApplicationList(int aiDocumentTypeId, int aiApplicationTypeId, int aiRequestTypeId)
{
try
{
//This is the magic potion...
List<int?> appmap = new List<int?>();
var applist = (from ram in applicationentity.ReqAppMaps
where ram.RequestTypeId == aiRequestTypeId
&& ram.IsActive == 1
select new AppMap
{
ApplicationTypeId = ram.ApplicationTypeId
}).ToList();
foreach (var item in applist)
{
appmap.Add(item.ApplicationTypeId);
}
//magic potion end
var applicationlist = new List<TicketApplication>();
using (var applicationentity = new eRequestsEntities())
{
applicationlist = (from app in applicationentity.Applications
where 1==1
===>>>&& appmap.Contains(app.ApplicationTypeId)<<<===
&& app.IsActive == 1
select new TicketApplication
{
ApplicationId = app.ApplicationId,
Description = app.Description,
DeliveryGroupId = app.DeliveryGroupId,
ApplicationTypeId =app.ApplicationTypeId,
DeliveryTypeId = app.DeliveryTypeId,
DocumentTypeId = app.DocumentTypeId,
SupportGroupId = app.SupportGroupId
}).OrderBy(a => a.Description).ToList();
return applicationlist;
}
A side-note, C# is a strongly-typed language, just make sure your data types matches during evaluation, as int? vs int etc.., will never compile. A small dose of LINQ is enough to send some newbies circling around for hours. One of my ID-10T programming experience but just enough to remind me that my feet's still flat on the ground.

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