Refactoring LINQ query - c#

Consider the below
if(type== "S")
{
lstItem.ItemsSource = (from item in Items
where item.Property1 == "SomeValue"
select item);
}
else
{
lstItem.ItemsSource = (from item in Items
where item.Property2 == "SomeOtherValue"
select item);
}
As can be figured out that the only difference between these two queries is only in the property name (for the first one it is Property1 & for the second it is Property2)
Is there any better way of refactoring / writing the code in a structured mannner(some common method where only the property name will be passed and the record will be filtered as per that) or this is the proper way of doing the same?
Need help.
Thanks

It is also possible to add an inline if in the where clause
lstItem.ItemsSource =
(from item in Items
where (test == "S" ? item.Property1 == "SomeValue" : item.Property2 == "SomeOtherValue")
select item);

You can chain your commands within if statements. E.g.:
var items = from item in Items
select item;
if(type== "S")
{
items = items.Where(item => item.Property1 == "SomeValue");
}
else
{
items = items.Where(item => item.Property2 == "SomeOtherValue");
}
Or even just write the tidier lambda structure in you orignal code:
if(type== "S")
{
lstItem.ItemsSource = Items.Where(item => item.Property1 == "SomeValue");
}
else
{
lstItem.ItemsSource = Items.Where(item.Property2 == "SomeOtherValue");
}

I like:
lstItem.ItemsSource = Items.Where(type == "S" ?
item => item.Property1 == "SomeValue":
item => item.Property2 == "SomeOtherValue");

well, you could start by boiling the expression down to:
Func<Items, bool> expr;
if(type== "S")
{
expr = (item => item.Property1 == "SomeValue");
}
else
{
expr = (item => item.Property2 == "SomeOtherValue");
}
var items = Items.Where(expr);
of course, the game plan is really to make it all a single statemnet, but this makes it a LITTLE more manageable i think :)
jim

Related

Remove a range of items from a list without looping

Hi Is there a more ellegant way of doing this must I do the loop is there like a range funciton I could just remove all the items found
Sorry I should have showing how my qry is being inserted.
Btw these are two different entities that I am removing from I hope you get the idea.
var qry = db.AssemblyListItems.AsNoTracking().Where(x =>
x.ProductionPlanID == (long)_currentPlan.ProductionPlan ).ToList();
var hasbeenAssembled = db.CompletedPrinteds.AsNoTracking().Where(x =>
x.ProductionPlanId == item.ProductionPlanID).ToList();
var hasbeenFound = db.CompletedPrinteds.AsNoTracking().Where(x =>
x.ProductionPlanId== item.ProductionPlanID).ToList();
foreach (var subitem in hasbeenAssembled )
{
if(item.ProductionPlanID ==subitem.ProductionPlanId && item.DocumentNo == subitem.DocumentNo && item.DocumentNo == subitem.DocumentNo && item.OutstandingToMake ==0)
{
qry.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanId && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode && item.OutstandingToMake ==0);
}
}
public List<AssemblyListItems> RemoveDespatchedItems(List<AssemblyListItems> AssemblyItems)
{
foreach (AssemblyListItems item in AssemblyItems)
{
using (var db = new LiveEntities())
{
var hasNotBeenDespatched = db.PalletizedItems.Where(w => w.Despatched != "Not Despatched");
foreach (var subitem in hasNotBeenDespatched)
{
AssemblyItems.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanID && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode);
}
}
}
return AssemblyItems;
}
I just need to remove the items from the first query hasNotBeenDespatched from the second query.As could be over 400 items i want it to be efficient as possible.
Edit 2
I am a we bit closer thanks buts its still not removing the items from the removedespatchitems from the assebmittems I do not no why
public List<AssemblyListItems> RemoveDespatchedItems(List<AssemblyListItems> AssemblyItems, Int64 ProductionPlanId)
{
using (var db = newLiveEntities())
{
List<PalletizedItems> removeDespatchItems = db.PalletizedItems.Where(w => w.Despatched != "Not Despatched" && w.ProductionPlanID == ProductionPlanId).ToList();
var itemsDocumentNo = db.PalletizedItems.Select(x => x.ProductionPlanItemID).ToList();
foreach (var subitem in removeDespatchItems) {
AssemblyItems.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanID && itemsDocumentNo.Contains(x.ProductionPlanItemID) && x.ItemCode == subitem.StockCode && x.LineQuantity==x.AllocatedQuantity);
}
}
return AssemblyItems;
}
Not 100% I get exactly how it should be.
However in general you could use join that would result in it being done in the database. Something like this:
var remainingItems = (from ali in db.FUEL_AssemblyListItems
join completed in db.FuelCompletedPrinteds
on new { ali.ProductionPlanID, ali.DocumentNo, ali.ItemCode } equals new { completed.ProductionPlanID, completed.DocumentNo, completed.StockCode }
join dispatched in db.FUEL_PalletizedItems
on new { ali.ProductionPlanID, ali.DocumentNo, ali.ItemCode } equals new { dispatched.ProductionPlanID, dispatched.DocumentNo, dispatched.StockCode }
where (ali.ProductionPlanID == (long) _currentPlan.ProductionPlan
&& ali.DocumentNo == completed.DocumentNo
&& completed.OutstandingToMake == 0
&& dispatched.Despatched != "Not Despatched")
select ali).ToList();
Depending upon the records in the database the join might need to be a outer join which needs a slightly different syntax but hopefully you've got a starting point.

filtering list inside forEach with multiple conditions

I have created c# method to filter list of students based on certain conditions.
I created filteredstudents list first to populate it after applying initial filter condition on students list.
Then i created another list filteredstudentsWithUnits to populate after applying filters on filteredstudents
I tried to make it shorter like using single list but couldn't come find the way.
Can someone suggest because i will have additional filter conditions so i really don't want to create multiple lists.
private List<StudentDTO> _validateStudents(List<StudentDTO> students)
{
List<StudentDTO> filteredstudents = new List<StudentDTO>();
foreach (StudentDTO student in students)
{
if (student.age != null && student.status != "DEL")
filteredstudents.Add(student);
else
_log("create log");
}
List<StudentDTO> filteredstudentsWithUnits = new List<StudentDTO>();
foreach (StudentDTO student in filteredstudents)
{
bool valid = true;
foreach (SubjectTypes subjectType in student.SubjectTypes)
{
string value1 = subjectType.SubjectItem.Select(x => x.value1).First();
Guid StId = _context.Items.Where(x => x.Name == value1).FirstOrDefault();
if (StId != null)
valid = true;
else
{
valid = false;
_log("create log");
}
}
if (valid)
filteredstudentsWithUnits.Add(student);
}
return filteredstudentsWithUnits;
}
If i read your code correctly you could use this short LINQ query:
List<StudentDTO> filteredstudentsWithUnits = students
.Where(s => s.age != null && s.status != "DEL")
.Where(s => !_context.Items
.Any(x => s.SubjectTypes
.SelectMany(t => t.SubjectItem.Select(si => si.value1))
.Contains(x.Name)))
.ToList();

MVC4 Linq Query Optimisation

I have the below code which works but I do not feel this is the best way to achieve the result. I am looking at optimising my code. Any suggestions of a better option will be appreciated. sub is a subcategory which is nullable.
[AllowAnonymous]
public ActionResult _relatedgrps(string cat, string sub)
{
if (!string.IsNullOrWhiteSpace(sub)){
var pgs = db.Pages
.Where(u=>u.MetaNoSearch==false)
.Where(u => u.PaOk == true && u.Category.Name == cat && u.SubCategory.CatName == sub)
.OrderByDescending(u => u.PaCreatedOn);
return PartialView(pgs.ToList());
}else{
var pgs = db.Pages
.Where(u=>u.MetaNoSearch==false)
.Where(u => u.PaOk == true && u.Category.Name == cat )
.OrderByDescending(u => u.PaCreatedOn);
return PartialView(pgs.ToList());
}}
Linq IEnumerables can be additive and the query will only be executed when enumerated for the first time (like calling .ToList()). So you should be able to do something like this:
var pgs = db.Pages
.Where(u => u.MetaNoSearch == false &&
u.PaOk == true &&
u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
{
pgs = pgs.Where(u => u.SubCategory.CatName == sub);
}
return PartialView(pgs.OrderByDescending(u => u.PaCreatedOn).ToList());
Create an object to query it. To improve it, you also could remove it boolean comparations because they are conditions.
var query = db.Pages.Where(u => !u.MetaNoSearch && u.PaOk && u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
query = query.Where(u => u.SubCategory.CatName == sub);
query = query.OrderByDescending(u => u.PaCreatedOn);
return PartialView(query.ToList());
#user3021830 - be careful with String.IsNullOrWhitespace, you cannot use that in a database query. You could do String.IsNullOrWhitespace(sub), but not String.IsNullOrWhitespace(u.*).
I'd avoid any conditionals in the query because that will likely result in a case statement in the SQL.
To produce the best SQL I'd do something like this:
var pgs = db.Pages.Where(u => u.MetaNoSearch == false)
.Where(u => u.PaOk == true)
.Where(u => u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
{
pgs = pgs.Where(u => u.SubCategory.CatName == sub);
}
var result = pgs.OrderByDescending(u => u.PaCreatedOn).ToList();

Get the specific element in nested list using lambda in c#

Good days,
Lets say I have a static List<AClass> object (lets name it myStaticList) which contains an other list inside and that list contains an other list with CId and Name property.
What I need to do is
foreach(AClass a in myStaticList)
{
foreach(BClass b in a.bList)
{
foreach(CClass c in b.cList)
{
if(c.CId == 12345)
{
c.Name = "Specific element in static list is now changed.";
}
}
}
}
Can I achieve this with LINQ Lambda expressions?
Something like;
myStaticList
.Where(a=>a.bList
.Where(b=>b.cList
.Where(c=>c.CId == 12345) != null) != null)
.something logical
.Name = "Specific element in static list is now changed.";
Please note that i want to change the property of that specific item in the static list.
You need SelectMany to flatten your lists:
var result = myStaticList.SelectMany(a=>a.bList)
.SelectMany(b => b.cList)
.FirstOrDefault(c => c.CId == 12345);
if(result != null)
result.Name = "Specific element in static list is now changed.";;
Use SelectMany (excellent post here)
var element = myStaticList.SelectMany(a => a.bList)
.SelectMany(b => b.cList)
.FirstOrDefault(c => c.CId == 12345);
if (element != null )
element.Name = "Specific element in static list is now changed.";
var item = (from a in myStaticList
from b in a.bList
from c in b.cList
where c.CID = 12345
select c).FirstOrDefault();
if (item != null)
{
item.Property = "Something new";
}
You can use SelectMany also, but it's not straightforward.

how to create dynamic linq query based on search criterias

I have a search form which i want to use to search a database for data. The searchbox has 4 checkboxes and 1 textfield. The problem is how do i build the linq query considering i dont know beforehand what textboxes the user will check for filtering the search. What i have so far is:
[HttpPost]
public ActionResult search(string ulv, string bjorn, string jerv, string gaupe)
{
var query = (from o in db.observasjonene select o);
if (ulv != null)
{
query = query.Where(o => o.art == ulv);
}
if (bjorn != null)
{
query = query.Where(o => o.art == bjorn);
}
if (jerv != null)
{
query = query.Where(o => o.art == jerv);
}
if (gaupe != null)
{
query = query.Where(o => o.art == gaupe);
}
IEnumerable ls = query.ToList();
return Json(ls, JsonRequestBehavior.AllowGet);
}
The problem with the "where" clause is that if a condition is true, it overwrites the results from the earlier condition. I guess i need an "or" statement or something..
If I have understood your question correctly, you want to check if art equals to any of provided values. You can combine those values into collection and check if collection contains art value:
var values = new [] { ulv, bjorn, jerv, game }.Where(v => v != null);
var query = from o in db.observasjonene
where values.Contains(o.art)
select o;
EF translates Contains into SQL IN operator.
I'm using two approaches in this case:
Build dynamic query:
var q = DB.Invoices.AsQueryable();
if (isPresented != null)
q = q.Where(iv => iv.IsPresented == isPresented);
if (ID != null)
q = q.Where(iv => iv.ID == ID.Value);
...........................
return from iv in q
orderby iv.DueDate descending
select iv;
Use Union to combine search results:
var q1 = db.FeeInvoice.Where(fi => [QUERY1]));
if (isPresented != null)
{
var q2 = db.FeeInvoice.Where(fi =>[QUERY2]));
q1.Union(q2);
}
if (ID != null)
{
var q3 = db.FeeInvoice.Where(fi =>[QUERY3]);
q1.Union(q3);
}
...........................
You are comparing all the parameters value to single column in the query ie. art (see you have written same column name in each where condition) . I'm not sure why are you doing so? you can simply take single parameter which compare the value like this
public ActionResult search(string value)
{
query = query.Where(o => o.art == value);
}
or if it is by mistake and you want to apply where condition along with multiple column then you can try something like this
query=query.Where(o => (o.art == ulv || ulv == string.Empty) && (o => o.bjorn == bjorn || bjorn=string.empty) && (o.jerv == jerv || jerv == string.Empty) && (o.gaupe == gaupe || gaupe == string.Empty));
Note: I assume your column name as your parameters name.

Categories

Resources