How to simplify this LINQ WHERE query? - c#

I'm trying to select several rows from a table but would like the selection to stop if the first where clause comes back with results. Basically if there are results with language then I don't need to query for DevLanguage or FallbackLanguage, is this possible in a single query with || operator or with some other LINQ magic?
var languageStrings = _presentationContext.LocalizationStrings
.Where(x => x.Namespace.Namespace == #namespace &&
x.Namespace.Language == language);
var devStrings = _presentationContext.LocalizationStrings
.Where(x => x.Namespace.Namespace == #namespace &&
x.Namespace.Language == Constants.DevLanguage);
var fallbackStrings = _presentationContext.LocalizationStrings
.Where(x => x.Namespace.Namespace == #namespace &&
x.Namespace.Language == Constants.FallbackLanguage);
var localizationStrings = languageStrings.Any() ? languageStrings : devStrings.Any() ? devStrings : fallbackStrings.Any() ? fallbackStrings

I would put the common part of these queries into a separate query.
var query = _presentationContext.LocalizationStrings.Where(x => x.Namespace.Namespace == #namespace);
var languageStrings = query.Where(x => x.Namespace.Language == language);
var devStrings = query.Where(x => x.Namespace.Language == Constants.DevLanguage);
var fallbackStrings = query.Where(x => x.Namespace.Language == Constants.FallbackLanguage);
The SQL is generated the same.

While there are probably more downsides to the code below than I know of, it's a one-liner (if you ignore the temp variable declaration) and only executes the LINQ queries if the previous one failed to return any results.
IEnumerable<dynamic> temp;
var localizationStrings =
(temp = _presentationContext.LocalizationStrings.Where(x =>
x.Namespace.Namespace == #namespace &&
x.Namespace.Language == language)).Any() ? temp :
(temp = _presentationContext.LocalizationStrings.Where(x =>
x.Namespace.Namespace == #namespace &&
x.Namespace.Language == Constants.DevLanguage)).Any() ? temp :
(temp = _presentationContext.LocalizationStrings.Where(x =>
x.Namespace.Namespace == #namespace &&
x.Namespace.Language == Constants.FallbackLanguage)).Any() ? temp :
Enumerable.Empty<string>();
One reason why I would either pick your original code or search for a different method is because assigning values in a chain like this is not performance friendly.

Related

LINQ Conditional OrderBy

I'm trying to do a conditional OrderBy but it's having no effect. The List outputs the same with default ordering.
I've tried both approaches suggested in this question Conditional "orderby" sort order in LINQ
var query = _context.Groups
.Where(gr => gr.Status != ((sbyte)ActiveStatus.DELETED)
&& gr.OrganisationId == user.OrganisationId
&& (search != null && gr.Name != null ? (gr.Name.Contains(search)) : true == true)
)
.Select(GroupReportModel.Projection);
if(!pager.Sort.HasValue || pager.Sort.Value == ((int)Sort.MODIFIED))
query.OrderByDescending(gr => gr.Created.Date);
if(pager.Sort.Value == ((int)Sort.NAME))
query.OrderByDescending(gr => gr.Name);
pager.TotalRecords = query.Count();
var list = query.Skip(pager.PageCount != null ? pager.PageCount.Value * (pager.Page.Value) : 0)
.Take(pager.PageCount != null ? pager.PageCount.Value : 0)
.ToList();
LINQ methods do not mutate the query object, they return a new one, you need to reassign it:
if(!pager.Sort.HasValue || pager.Sort.Value == ((int)Sort.MODIFIED))
query = query.OrderByDescending(gr => gr.Created.Date);
if(pager.Sort.Value == ((int)Sort.NAME))
query = query.OrderByDescending(gr => gr.Name);
....

How to write a linq query to exclude some of the records?

This is my LINQ
IList<string> ExceptList = new List<string>() { "045C388E96", "C9B735E166", "02860EB192", "2401016471" };
var listusers = context.USER_INFO.Where(x => x.ACTIVATED
&& x.COMP.EQUIPMENT.Count(y => y.STATUS == (int)STATUSEQ.ACTIVE) > 0
&& (x.LAST_LOGIN < time)
&& !ExceptList.Contains(x.COMP.CODE)
&& !x.IS_LOCK
|| !x.COMP.IS_LOCK)
.Select(x => new EmailOutOfDateLoginModel
{
COMPCode = x.COMP.CODE,
First_Name = x.FIRST_NAME,
Last_Name = x.LAST_NAME,
Total_EQ = x.COMP.EQUIPMENT.Count(y => y.STATUS == (int)STATUSEQ.ACTIVE),
User_Email = x.USER_EMAIL
}).ToList();
I am not sure why my ExceptList is not working. I want to exclude any record that contaisn any of the CODE in the ExceptList
Put parentheses around the expressions containing the && logic. The || at the end is only matched with the !x.IS_LOCK || !x.COMP.IS_LOCK otherwise.
According your linq all records where (!x.COMP.IS_LOCK==true) will be included in the query. Try this "where" part:
.Where(x => x.ACTIVATED
&& x.COMP.EQUIPMENT.Count(y => y.STATUS == (int)STATUSEQ.ACTIVE) > 0
&& (x.LAST_LOGIN < time)
&& !ExceptList.Contains(x.COMP.CODE)
&& !(x.IS_LOCK && x.COMP.IS_LOCK))

LINQ Query with method syntax

My requirement is to make boolean value (IsPC=true) only if I found any value with IsCurrent = true from the list and second condition is to filter the list with G or W codes and third condition is to check the PCBNumber length ==15 with only one from the list.
How short can i able to reduce the below query using LINQ method syntax
below is my query
var CurrentQ= p.List.Where(x => x.IsConCurrent== true);
if (CurrentQ.Count() > 0)
{
var NCurrentQwithWorQ = p.List.Where(x => x.Codes == Codes.W|| x.Codes== Codes.Q).Count();
if (NCurrentQwithWorQ != null)
{
var PCBNumber = p.List.Where(x => x.PCBNumber .Length == 15).Count();
if (PCBNumber == 1)
{
isPC = true;
}
}
}
You can use all conditions in same query like below,
var PCBNumber= p.List.Where(x => x.IsConCurrent== true && (x.Codes == Codes.W|| x.Codes== Codes.Q) && x.PCBNumber.Length == 15);
if (PCBNumber !=null && PCBNumber.Count() == 1)
{
isPC = true;
}
I'm not trying to debug what you wrote, but isn't this really what you're looking for--that is, daisy-chaining your Where conditions?
var isPC = p.List.Where(x => x.IsConCurrent == true).Where(x => x.Codes == Codes.W || x.Codes == Codes.Q).Where(x => x.PCBNumber.Length == 15).Count() == 1;
Both solutions suggested above are correct.
p.List.Where(x => x.IsConCurrent== true && (x.Codes == Codes.W|| x.Codes== Codes.Q) && x.PCBNumber.Length == 15);
p.List.Where(x => x.IsConCurrent == true).Where(x => x.Codes == Codes.W || x.Codes == Codes.Q).Where(x => x.PCBNumber.Length == 15).Count()
Actually they are performed in the same way. The Where function does not force immediate iteration through the data source. Only when you execute the Count function, LINQ will process row by row and execute criterion by criterion to find out which values should be calculated.
I can only suggest you add the Take(2) operator after the where clause. In this case LINQ will stop after finding the first two rows that matches provided criterion and other rows will not be processed.
p.List.Where(x => x.IsConCurrent == true)
.Where(x => x.Codes == Codes.W || x.Codes == Codes.Q)
.Where(x => x.PCBNumber.Length == 15)
.Take(2).Count()

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

C# Linq syntax help - How can I exclude two values?

I have this linq query that works well (although it may be written better, pls say so if you notice something)
var qry = BenefitCodes
.Where(b => b.BenInterest != 'E'
&& (b.BenProductLine == CoverageProductLine || b.BenProductLine == null) )
.Select(b => b)
.OrderBy(b => b.BenDesc);
A new requirement came down the pipeline to exclude two BenCodes ( 1001, 1009), BenCodes is just another column in the SQL table.
Am I supposed to use some variation of ".Contains", I would have to do !Contains or something. Can anyone point me in the right direction?
Thanks,
~ck in San Diego
Yes, one way to handle this is the following (for brevity and readability, I am excluding the remainder of your query):
var excludedBenCodes = new List<int>() { 1001, 1009 };
var query = BenefitCodes.Where(b => !excludedBenCodes.Contains(b.BenCodes));
I believe this to be more readable and more maintainable than the alternative of adding a subclause b.BenCodes != 1001 && b.BenCodes != 1009 to your where clause.
You could just simply add another line to the Where clause and exclude every itm with a BenCodes of 1001 or 1009.
Like this:
var qry =
BenefitCodes
.Where(b =>
b.BenInterest != 'E' &&
(b.BenProductLine == CoverageProductLine || b.BenProductLine == null) &&
b.BenCodes != 1001 &&
b.BenCodes != 1009)
.Select(b => b)
.OrderBy(b => b.BenDesc);
This might make things a bit more readable, I'd change query to
var qry = BenefitCodes.Where(b => FitsCriteria(b)).OrderBy(b => b.BenDesc);
and add method
public bool FitsCriteria(BenefitCode b)
{
return b.BenInterest != 'E' &&
(b.BenProductLine == CoverageProductLine || b.BenProductLine == null) &&
b.BenCodes != 1001 &&
b.BenCodes != 1009;
}
Kindness,
Dan
var qry = BenefitCodes
.Where(b => b.Code != '1001'
&& b.Code != '1009'
&& b.BenInterest != 'E'
&& ( b.BenProductLine == CoverageProductLine
|| b.BenProductLine == null))
.OrderBy(b => b.BenDesc);
You don't even need the "Select" when you aren't using the LINQ syntax. as the Where and OrderBy methods already return your IQueryable.

Categories

Resources