How to convert IQueryable<T> to Expression<Func<T, bool>>? - c#

I just want to build a dynamic filters.
And finally to return
Expression<Func<Event, bool>>
I've tried to use the Combine (AndAlso) expressions, but it wasn't workin and finally I found that there are IQueryable queries which works good, but now how can I convert it to the return type -
Expression<Func<Event, bool>>?
My code:
public IQueryable<Event> GetBySearch(EventFilter search)
{
IQueryable<Event> query = this.Context.Events.AsQueryable();
Expression<Func<Event, bool>> expression = null;
if (search.CategoryId != 0)
{
query = query.Where(x => x.CategoryId == search.CategoryId);
}
if (search.SubCategoryId != 0)
{
query = query.Where(x => x.SubCategoryId == search.SubCategoryId);
}
expression = query.Expression as Expression<Func<Event, bool>>; //This convert is not working, it returns null.
return this.Context.Events.Where(expression);
}

Any reason you don't just do the following:
public IQueryable<Event> GetBySearch(EventFilter search)
{
IQueryable<Event> query = this.Context.Events.AsQueryable();
if (search.CategoryId != 0)
{
query = query.Where(x => x.CategoryId == search.CategoryId);
}
if (search.SubCategoryId != 0)
{
query = query.Where(x => x.SubCategoryId == search.SubCategoryId);
}
return query;
}
As Florian said in the comment, returning IQueryables is to be avoided (when possible). The easy solution is to return a list instead:
public List<Event> GetBySearch(EventFilter search)
{
IQueryable<Event> query = this.Context.Events.AsQueryable();
if (search.CategoryId != 0)
{
query = query.Where(x => x.CategoryId == search.CategoryId);
}
if (search.SubCategoryId != 0)
{
query = query.Where(x => x.SubCategoryId == search.SubCategoryId);
}
return query.ToList();
}

This conversion is not valid because Where converts it into a MethodCallExpression
This would be valid:
MethodCallExpression e = query.Expression as MethodCallExpression;

Related

Linq if else condition

How can I merge this below query into a single query? I think multiple if's will add unnecessarily to code complexity.
var query = scenario.Where(x => x.ScenarioNumber == ScenarioNumber).Select(x => x).Distinct().ToList();
// Match exception number else return wild card entries.
if(query.Any(x=>x.ExcepNumber == ExcepNumber))
{
query = query.Where(x => x.ExcepNumber == ExcepNumber).ToList();
}
else
{
query = query.Where(x => x.ExcepNumber == "**").ToList();
}
// Match regime else return wild card entries.
if (query.Any(x => x.Regime == Regime))
{
query = query.Where(x => x.Regime == Regime).ToList();
}
else
{
query = query.Where(x => x.Regime == "**").ToList();
}
finalResponse = query.Select(x => x.TestColumn).Distinct().ToList();
I have a suggestion. You could extract it into a generic method, in which you would pass a selector function which specifies the property to be used for the filtering and the 2 choices that you would like to be compared (and the collection of course).
private List<T> EitherFilterOrWildCard<T>(Func<T, string> selector, string compare, string elseCompare, List<T> query)
{
var temp = query.Where(x => selector(x) == compare);
return temp.Any() ? temp.ToList() : query.Where(x => selector(x) == elseCompare).ToList();
}
Your if statements would then shrink to these 2 lines:
query = EitherFilterOrWildCard<MyClass>(x => x.ExcepNumber, ExcepNumber, "**", query);
query = EitherFilterOrWildCard<MyClass>(x => x.Regime, Regime, "**", query);

Dynamic Expression Parameter

How can i make dynamic expression parameter ?
i was try
public List<Station> GetStationsByTimeAndState(StationState stationState, int? categoryId = null, int? provinceId = null, int? districtId = null)
{
List<Station> stations;
Expression<Func<Station, bool>> exp = p => p.StationState == stationState;
if(categoryId==null){
exp+= p.CategoryId==categoryId;//or exp.Add()//exp.Update()
}
.....
return stations = stationDal.GetList(exp);
}
The GetList method calls the Where query that belongs to Linq in itself.
What I want to do is dynamically constructing and sending the expression inside the Where query.
You need to build a Linq Expression Tree for your example this would look like this:
// Parameter: p
Expression<Func<Station, bool>> exp;
var parameterExpression = Expression.Parameter(typeof(Station));
// p == stationState
var equalsStationState = Expression.Equal(
Expression.Property(stateParameterExpression, nameof(Station.StationState))
Expression.Constant(stationState));
if (categoryId != null)
{
// p.CategoryId == categoryId
var equalsCategory = Expression.Equal(
Expression.Property(stateParameterExpression, nameof(Station.CategoryId)),
Expression.Constant(categoryId));
// (p == stationState) && (p.CategoryId == categoryId)
var andExpression = Expression.AndAlso(equalsStationState, equalsCategory);
// p => (p == stationState) && (p.CategoryId == categoryId)
exp = predicate = Expression.Lambda(
andExpression,
stateParameterExpression);
}
The code above is not complete, but you can get an idea on how it works.
As an alternative you could conditionally chain Where methods, which would be much simpler:
IEnumerable<StationList> stations = allStations
.Where(s => s.StationState == stationState);
if (categoryId != null)
{
stations = stations.Where(s => s.CategoryId == category)
}
return stations;

How to tell LINQ to skip where if condition is false?

Refer to code below:
Instead of having so many if else statements:
List<T> GetTs(string createBy, bool isAdmin, string departmentCode, ...)
{
if(isAdmin)
{
if(!string.IsNullOrEmpty(createBy))
{
var query = context.table
.Where(x => string.compare(x.createBy, createBy, StringComparison.OrdinalIgnoreCase) == 0)
.ToList();
return query;
}
else
{
var query = context.table.ToList();
return query;
}
}
else
{
if(!string.IsNullOrEmpty(createBy))
{
var query = context.table
.Where(x => string.compare(x.departmentCode, departmentCode, StringComparison.OrdinalIgnoreCase) == 0)
.Where(x => string.compare(x.createBy, createBy, StringComparison.OrdinalIgnoreCase) == 0)
.ToList();
return query;
}
else
{
var query = context.table
.Where(x => string.compare(x.departmentCode, departmentCode, StringComparison.OrdinalIgnoreCase) == 0)
.ToList();
return query;
}
}
}
Can I do this instead where it don't have so many if else statements:
var query = context.table
.Where(x => (!isAdmin)? string.compare(x.departmentCode, departmentCode, StringComparison.OrdinalIgnoreCase) == 0) : SKIP THIS '.Where')
.Where(x => (!string.IsNullOrEmpty(createBy))? string.compare(x.createBy, createBy, StringComparison.OrdinalIgnoreCase) == 0) : SKIP THIS '.Where')
//...
.ToList();
How do I tell LINQ to skip the .Where if condition is false? possible?
Suggestion of Patrick Hofman from comment below works:
var query = context.table
.Where(x => (!isAdmin)? string.compare(x.departmentCode, departmentCode, StringComparison.OrdinalIgnoreCase) == 0) : true)
.Where(x => (!string.IsNullOrEmpty(createBy))? string.compare(x.createBy, createBy, StringComparison.OrdinalIgnoreCase) == 0) : true)
//...
.ToList();
If I understand the intent (to conditionally apply predicates), then basically: do the composition differently:
IQueryable<Whatever> query = context.Table;
if(condition1)
query = query.Where(x => x.column1);
if(condition2)
query = query.Where(x => x.column2);
//...etc
var list = query.ToList();
You could probably wrap that in an extension method like
static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition,
Expression<Func<T,bool>> predicate)
=> condition ? query.Where(predicate) : query;
and:
var list= context.Table.WhereIf(condition1, x => x.column1)
.WhereIf(condition2, x => x.column2)
// ...
.ToList();
however, I probably wouldn't do that, as in many cases it will require constructing an unnecessary additional expression tree.

Entity Framework Dynamic Query Expression being ignored

I have an operation that takes a serializable QueryModel and converts it to an Expression to be passed to Entity Framework. My query against the database looks like:
public IEnumerable<PhotoVerifySessionOverview> FindSessions(Expression<Func<vwPhotoVerifySession, bool>> predicate, PaginationModel model)
{
var sessions = Context.vwPhotoVerifySessions
.AsQueryable()
.Where(predicate)
.OrderBy(string.Format("{0} {1}", model.OrderByColumn, model.OrderByDirection))
.Skip(model.Offset)
.Take(model.PageSize);
return Mapper.Map<IEnumerable<PhotoVerifySessionOverview>>(sessions);
}
and my predicate builder looks like:
var predicate = PredicateBuilder.True<vwPhotoVerifySession>();
//Add the tenant to the where clause
if (model.TenantId.HasValue)
predicate.And(p => p.TenantId == model.TenantId.Value);
else
predicate.And(p => p.TenantReferenceId == model.TenantReferenceId);
//Add a date range if one is present
if (model.CreatedOnRange != default(DateRange))
{
var endDate = model.CreatedOnRange.End == default(DateTime) ? DateTime.Now : model.CreatedOnRange.End;
predicate.And(p => p.CreatedOn >= model.CreatedOnRange.Start && p.CreatedOn <= endDate);
}
//Include status filtering if any filters are present
if (model.StatusFilter != null && model.StatusFilter.Any())
{
//use Id and name to search for status
predicate.And(p => model.StatusFilter.Any(f => f.StatusId == p.StatusId || p.Status == f.Name));
}
var pagination = model as PaginationModel;
var sessions = Manager.FindSessions(predicate, pagination);
return sessions;
The problem is, my Where clause is not being evaluated and all results are being returned. Is there something else I should be doing to get the Where statement to work correctly?
You need to assign predicate back to itself.
predicate = predicate.And(p => p.TenantId == model.TenantId.Value);

Linq Dynamically append Where clauses with OR

I've been trying to build a linq query with dynamically added where clauses. I have a web page with a bunch of checkboxes that are selected to correspond to which fields you want to search:
What I have so far is the following:
//This calls a select from table to construct the query which the where clauses will be added to
IQueryable<AutoCompleteRestultDto> query = GetAutocompleteResults();
if (FirstName == true || AllFields == true)
{
Expression<Func<AutoCompleteRestultDto, bool>> firstNameFilter = c => terms.Any(t => c.FirstName.ToLower().Contains(t.ToLower()));
query = query.Where(firstNameFilter);
}
if (LastName == true || AllFields == true)
{
Expression<Func<AutoCompleteRestultDto, bool>> lastNameFilter = c => terms.Any(t => c.LastName.ToLower().Contains(t.ToLower()));
query = query.Where(lastNameFilter);
}
if (KnownAs == true || AllFields == true)
{
Expression<Func<AutoCompleteRestultDto, bool>> knownAsFilter = c => terms.Any(t => c.KnownAs.ToLower().Contains(t.ToLower()));
query = query.Where(knownAsFilter);
}
// etc.
return query
.Select(c => new ContactAutoCompleteModel
{
label = c.FirstName + " " + c.LastName
})
.Take(15)
.OrderBy(d => d.label)
.ToList();
The problem is this solution requires all the expressions that are tacked on to simultaneously be true ie: where(clause1 AND clause2 AND clause3)
Can't figure out how to change this to OR clauses ie: where(clause1 OR clause2 OR clause3)
You are chaining Enumerable.Where calls in the code you posted. That's why you get the AND effect. This below is a poach of PredicateBuilder using predicates instead of expressions.
public static class PredicateExtensions
{
public static Predicate<T> Or<T> (this Predicate<T> p1, Predicate<T> p2)
{
return obj => p1(obj) || p2(obj);
}
public static Predicate<T> And<T> (this Predicate<T> p1, Predicate<T> p2)
{
return obj => p1(obj) && p2(obj);
}
public static Predicate<T> False<T> () { return obj => false; }
public static Predicate<T> True<T> () { return obj => true; }
public static Predicate<T> OrAll<T> (IEnumerable<Predicate<T>> conditions)
{
Predicate<T> result = PredicateExtensions.False<T>();
foreach (Predicate<T> cond in conditions)
result = result.Or<T>(cond);
return result;
}
public static Predicate<T> AndAll<T> (IEnumerable<Predicate<T>> conditions)
{
Predicate<T> result = PredicateExtensions.True<T>();
foreach (Predicate<T> cond in conditions)
result = result.And<T>(cond);
return result;
}
}
You can use the above like so with an enumerable , customizing (apriori) the predicate enumerable on some condition:
Predicate<AutoCompleteRestultDto> firstNamePredicate =
c => terms.Any(t => c.FirstName.ToLower().Contains(t.ToLower()));
Predicate<AutoCompleteRestultDto> lastNamePredicate =
c => terms.Any(t => c.LastName.ToLower().Contains(t.ToLower()));
Predicate<AutoCompleteRestultDto> knownAsPredicate =
c => terms.Any(t => c.KnownAs.ToLower().Contains(t.ToLower()));
var all = new Predicate<AutoCompleteRestultDto>[] {
firstNamePredicate,
knownAsPredicate,
lastNamePredicate };
//
var items = query.Where(a => PredicateExtensions.OrAll(all)(a)).ToList();
items = query.Where(a => PredicateExtensions.AndAll(all)(a)).ToList();
or adding them iteratively like you do, step by step:
Predicate<AutoCompleteRestultDto> orResultPredicate =
PredicateExtensions.False<AutoCompleteRestultDto>();
if (FirstName == true || AllFields == true) {
orResultPredicate=orResultPredicate.Or(firstNamePredicate); }
if (LastName == true || AllFields == true) {
orResultPredicate = orResultPredicate.Or(lastNamePredicate); }
if (KnownAs == true || AllFields == true) {
orResultPredicate = orResultPredicate.Or(knownAsPredicate); }
Func<AutoCompleteRestultDto, bool> funcOr = c => orResultPredicate(c);
//
IQueryable<AutoCompleteRestultDto> query; // initialized already
var items = query.Where(funcOr).ToList();

Categories