I have the following code which returns correctly IF I have ALL four strings filled. However, if one of those strings is empty, the list returned is empty. Basically, I need it to return a list even if 1 or more or even ALL of the strings are empty.
private List<Search> FilterSearchResults(List<Search> results)
{
string _dataType = cmbISDataType.SelectedItem.ToString();
string _medium = cmbISMedium.SelectedItem.ToString();
string _pStatus = cmbISPStatus.SelectedItem.ToString();
string _rStatus= cmbISRStatus.SelectedItem.ToString();
return results
.Where(a => a.Data_Type == _dataType && !string.IsNullOrWhiteSpace(_dataType))
.Where(b => b.Medium == _medium && !string.IsNullOrWhiteSpace(_medium))
.Where(c => c.PStat== _pStatus && !string.IsNullOrWhiteSpace(_pStatus ))
.Where(d => d.RStatus== _rStatus && !string.IsNullOrWhiteSpace(_rStatus))
.ToList();
}
Thanks in advance.
Rather than checking whether or not the value is null in every single iteration of the loop, only perform the check when the string is not null:
private List<Search> FilterSearchResults(List<Search> results)
{
string _dataType = cmbISDataType.SelectedItem.ToString();
string _medium = cmbISMedium.SelectedItem.ToString();
string _pStatus = cmbISPStatus.SelectedItem.ToString();
string _rStatus = cmbISRStatus.SelectedItem.ToString();
IEnumerable<Search> query = results;
if (!string.IsNullOrWhiteSpace(_dataType))
query = query.Where(a => a.Data_Type == _dataType);
if (!string.IsNullOrWhiteSpace(_medium))
query = query.Where(b => b.Medium == _medium);
if( !string.IsNullOrWhiteSpace(_pStatus))
query = query.Where(c => c.PStat == _pStatus);
if( !string.IsNullOrWhiteSpace(_rStatus))
query = query.Where(d => d.RStatus == _rStatus);
return query.ToList();
}
Your current condition specifies that any of the strings can't be null or empty which is why the list is being returned as empty. A where clause in Linq works by returning any object in the collection that satisfies the specified condition. If you were to specify the condition as simply:
results.Where(true);
All objects would be returned.
Using an OR instead of an AND for the is null or empty check will return any object in the list that evaluates true for either of conditions. Therefore if the filter string is empty all objects will be returned, otherwise only the objects which meet the other conditions will be returned.
Update your filters to:
return results
.Where(a => a.Data_Type == _dataType || string.IsNullOrWhiteSpace(_dataType))
.Where(b => b.Medium == _medium || string.IsNullOrWhiteSpace(_medium))
.Where(c => c.PStat== _pStatus || string.IsNullOrWhiteSpace(_pStatus ))
.Where(d => d.RStatus== _rStatus || string.IsNullOrWhiteSpace(_rStatus))
.ToList();
Related
This question already has answers here:
Linq dynamically adding where conditions
(3 answers)
Closed 2 years ago.
I am using entity framework and i want to add a where condition only if the condition is met else i dont want that where to be executed which i want to do it in 1 query
Context.Test
.Where(v => v.Valuation.ValuationPeriod.PeriodName == periodName && parameters.SelectedTemplateIds.Contains(v.TemplateTypeId) && fundBusinessIds.Contains(v.Fund.BusinessId))
.Where(v => parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode))
.Where(v => wpGroup == null || v.Valuation.PortfolioCompany.WPGroup == wpGroup)
.Where(v => !myDeals || v.Valuation.PortfolioCompany.UserPermission.Any(up => up.UserId == userId))
so in the above query
Where(v => parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode))
I want to execute only if
parameters.sortedList.Count() > 0
else i dont want to include that where condition in the above query. So basically here i know we can do by seperating the query that is first get a list of values and then check this condition and if it matches then add a where on that list. But is it possible without doing that?
The filter could be added contitionally as follows by using an intermediate assignment.
var result = Context.Test.Where(v => v.Valuation.ValuationPeriod.PeriodName == periodName && parameters.SelectedTemplateIds.Contains(v.TemplateTypeId) && fundBusinessIds.Contains(v.Fund.BusinessId));
if (parameters.sortedList.Count() > 0)
{
result = result.Where(v => parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode));
}
result = result.Where(v => wpGroup == null || v.Valuation.PortfolioCompany.WPGroup == wpGroup)
.Where(v => !myDeals || v.Valuation.PortfolioCompany.UserPermission.Any(up => up.UserId == userId));
You can use this
.Where(v => parameters.sortedList.Count() > 0 ?
parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode) : true)
LINQ queries don't execute unless you do something that causes enumeration of the resulting IEnumerable, which means you can have a pattern of:
var x = collection.Where(...);
if(sometest)
x = x.Where(..);
var result = x.ToList(); //enumerate, causing evaluation
If sometest is true then both the Where will apply (AND style; both where predicates would have to result in a true for a collection entity to appear in the result), if sometest is false, then only the first Where applies
You can add an extension
public static class WhereIfExtension
{
public static IEnumerable<TCollection> WhereIf<TCollection>(this IEnumerable<TCollection> source,
bool condition, Func<TCollection, bool> predicate)
{
return condition ? source.Where(predicate) : source;
}
public static IQueryable<TCollection> WhereIf<TCollection>(this IQueryable<TCollection> source, bool condition,
Expression<Func<TCollection, bool>> predicate)
{
return condition ? source.Where(predicate) : source;
}
}
then
Context.Test
.Where(v => v.Valuation.ValuationPeriod.PeriodName == periodName && parameters.SelectedTemplateIds.Contains(v.TemplateTypeId) && fundBusinessIds.Contains(v.Fund.BusinessId))
.WhereIf(parameters => parameters.sortedList.Count() > 0, parameters.sortedList.Contains(v.Valuation.PortfolioCompany.DealCode))
.Where(v => wpGroup == null || v.Valuation.PortfolioCompany.WPGroup == wpGroup)
.Where(v => !myDeals || v.Valuation.PortfolioCompany.UserPermission.Any(up => up.UserId == userId))
That My Linq query
var result = db.APPLICATIONS
.Where(a => Statuses.Contains(a.STATUS_ID))
.Where(a => a.TrackingNo == TrackingNo)
Statuses is a int list and TrackingNo is a nullable int (int?).
Problem:
If the TrackingNo is null then i dont want to run this clause or just skip this condition.
LINQ queries can be built in multiple steps:
var result = db.APPLICATIONS
.Where(a => Statuses.Contains(a.STATUS_ID));
if (TrackingNo != null)
{
result = result.Where(a => a.TrackingNo == TrackingNo);
}
Note that if you have a Select (a projection), you probably must build the query in multiple steps in multiple variables:
var result2 = result.Select(a => new { a.STATUS_ID });
with the result2 "built" after the if.
You can check a nullable int by using its "HasValue" property.
var result = db.APPLICATIONS
.Where(a => Statuses.Contains(a.STATUS_ID))
.Where(a => a.HasValue && (a.TrackingNo == TrackingNo))
This will cause it to evaluate the "HasValue" prior to checking the value itself. If HasValue return false, then it will never evaluate the rest of the expression (and thus not cause NullReferenceException).
If it is of type "int?", then this will work.
Just add && condition and check null. And you can use 1 where condiiton here why second where.Pls try this:
var result = db.APPLICATIONS
.Where(a => Statuses.Contains(a.STATUS_ID)
&& a.TrackingNo!=null
&& a.TrackingNo == TrackingNo)
You should first check the values of the filtering parameters before trying to add more stuff to the store expression. This would only apply the Statuses and TrackingNo filtering if the nullable TrackingNo has a value. Otherwise it will return all APPLICATIONS as IQueryable.
var result = db.APPLICATIONS.AsQueryable();
if (TrackingNo.HasValue)
{
result = result.Where(a => Statuses.Contains(a.STATUS_ID) && a.TrackingNo == TrackingNo);
}
return result;
Alternatively, this would check if you have any statuses to apply and the tracking separatedly.
var result = db.APPLICATIONS.AsQueryable();
if (Statuses != null && Statuses.Count() > 0)
{
result = result.Where(a => Statuses.Contains(a.STATUS_ID));
}
if (TrackingNo.HasValue)
{
result = result.Where(a => a.TrackingNo == TrackingNo);
}
return result;
Or third option, as it is unclear what you really wanted. This would apply the statuses filtering always and tracking only if it is available
var result = db.APPLICATIONS.Where(a => Statuses.Contains(a.STATUS_ID));
if (TrackingNo.HasValue)
{
result = result.Where(a => a.TrackingNo == TrackingNo);
}
return result;
another kinda newbie question I guess. I have EF setup and now I want to select some records based on a filter. I have SomeClass with 4 items (all strings to keep things simple, lets call them string1, string2, and so on). Now, in a post I send the filter in an instance of SomeClass, but maybe not all properties are filled in.
So you might end up with string1="something", string2="bla" and string4="bla2". So string 3 = null. Now, how do I setup the query? If i try something like:
var dataset = entities.mydatabase
.Where(x => x.string1 == someclass.string1 && x.string2 == someclass.string2 && x.string3 == someclass.string3 && x.string4 == someclass.string4)
.Select(x => new { x.string1, x.string2, x.string3, x.string4}).ToList();
... I get no results, because string3=null. I could do something with checking all parameters and see if they're set and create the query based on that, but there must be something more elegant than that.
Anyone?
Thanks!
Ronald
The following will return all rows where the someclass.string is null OR equals to x.string.
var dataset = entities.mydatabase
.Where(x => someclass.string1 == null || x.string1 == someclass.string1)
.Where(x => someclass.string2 == null || x.string2 == someclass.string2)
.Where(x => someclass.string3 == null || x.string3 == someclass.string3)
.Where(x => someclass.string4 == null || x.string4 == someclass.string4)
.Select(x => new { x.string1, x.string2, x.string3, x.string4}).ToList();
I have the following two statements :-
var isadminByuser = tms.SecurityRoles.Where(a => a.Name.ToLower() == "administrator")
.Select(a=>a.SecurityRoleUsers.Where(a2 => a2.UserName.ToLower() == user.ToLower()));
if (isadminByuser.Count() >= 1) { return true;}
&
var adminByGroup = tms.SecurityRoles.Where(a => a.Name == "Administrator")
.SingleOrDefault().Groups
.Select(a2 => a2.TMSUserGroups
.Where(a3 => a3.UserName.ToLower() == user.ToLower()));
bool isadminByGroup = adminByGroup.Count() >= 1;
The first var isadminByuser will always have elements (will always be >=1), even if the where clause a3.UserName.ToLower() == user.ToLower())) is false, while the other var will have a count of zero is the where clause (a3.UserName.ToLower() == user.ToLower())) is false. So why will the first var never have a count of zero?
Thanks
The answer to the question asked is that you are selecting an IQueryable<SecurityRoleUsers> when you select
a.SecurityRoleUsers.Where(a2 => a2.UserName.ToLower() == user.ToLower())
which may or may not have a count of 0, but the containing query will return one of these IQueryables for each SecurityRole that matches Name = "administrator", hence the count will always be 1+ if there is at least one matching SecurityRole
Update:
// first get SecurityRoles
bool isadminByuser = tms.SecurityRoles.Where(a => a.Name.ToLower() == "administrator")
// now you're only interested in the related SecurityRoleUsers
.SelectMany( a => a.SecurityRoleUsers )
// now check if any of the SecurityRoleUsers meet your criteria
.Any( sru => sru.UserName.ToLower() == user.ToLower() );
Could you observe sql query differences from ms-sql-profiler
tms.SecurityRoles.Where(a => a.Name.ToLower() == "administrator")
.Select(a=>a.SecurityRoleUsers.Where(a2 => a2.UserName.ToLower() == user.ToLower()));
***
tms.SecurityRoles.Select(a=>a.SecurityRoleUsers.Where(a2 => a2.UserName.ToLower() == user.ToLower())).Where(a => a.Name.ToLower() == "administrator");
I have a 2 collection of diff types. i want to match a string in those collection and return the collection which did not match.
1) ac_CategoryList
2) mw_CharityList
would like to match if ac_CategoryList.Title is there in mw_CharityList.EntryTitle. if it is not there, than return the ac_CategoryList collection items which are not matched. and return one more collection of mw_CharityList type which matched in ac_CategoryList.Title. because i need to update the status in mw_CharityList collection.
var var charityList = _db.mw_CompetitionsEntry.Where(e => e.IsInvalid == false && e.IsPublished).ToList(); // first get the entire valid collection
var categoryList = _db.ac_Category.Where(c => c.Title != null && c.IsDeleted == false).ToList(); // get the entire valid collection
var titleNotExitsCollection = categoryList.Where(c => charityList.Any(e => e.EntryTitle.Trim() != c.Title.Trim())).ToList();
var titleExitsCollection = charityList.Where(e => categoryList.Any(c => c.Title.Trim() == e.EntryTitle.Trim())).ToList();
right now titleNotExitsCollection and titleExitsCollection returns the same no of records. i dont know what i am doing wrong... please help
Looks like there is a not operator missing, try:
var titleNotExitsCollection = categoryList.Where(c => !charityList.Any(e => e.EntryTitle.Trim() == c.Title.Trim())).ToList();
var commonTitles = categoryList.Select(x=>x.Title.Trim())
.Intersect(charityList.Select(x=>x.EntryTitle.Trim()));
var titleNotExitsCollection = categoryList.Where(x=>!commonTitles.Contains(x.Title))
.ToList();
var titleExitsCollection = charityList.Where(x=>commonTitles.Contains(x.EntryTitle))
.ToList();