using QueryOver's query separated - c#

How can I use QueryOver's linq query separated like below ICriteria which will be used in a generic method?
ICriterion c = Restrictions.And(Restrictions.Eq("Name", "Foo"),
Restrictions.Or(Restrictions.Gt("Age", 21), Restrictions.Eq("HasCar",true)));
IQueryOver<Foo> c = session.QueryOver<Foo>().Where((k => k.Name == "Tiddles"
&& k => k.Age== 21) || k => k.Age < 21);
public static IList<T> All(ICriterion c)
{
using (var session = NHibernateHelper<T>.OpenSession())
{
var CC = session.CreateCriteria(typeof(T));
CC.Add(c);
return CC.List<T>();
}
}

The equivalent to the first line, in lambda form is
ICriterion cr = Restrictions.Where<Foo>(k => k.Name == "Foo" && (k.Age > 21 || k.HasCar));

You could pass in a Expression<Func<T, bool>> which is what one of the Where overloads accepts instead of a Criteria.
System.Linq.Expressions.Expression<Func<Foo, bool>> c = k =>
(k.Name == "Tiddles" && k.Age== 21) || k.Age < 21;
public static IList<T> All(Expression<Func<T, bool>> expression)
{
using (var session = NHibernateHelper<T>.OpenSession())
{
return session.QueryOver<T>()
.Where(expression)
.List();
}
}

Related

LinqKit Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression2' to type 'System.Linq.Expressions.LambdaExpression'

I have a problem with Expressions. I have this lambda LINQ query with Entity Framework Core LINQ Provider:
IQueryable<ProcessValueBase> valuesSubquery;
switch (req.Period)
{
case TimePeriodType.Current:
valuesSubquery = dbContext.ProcessValues;
break;
case TimePeriodType.Day:
case TimePeriodType.Week:
case TimePeriodType.Month:
valuesSubquery = dbContext.NormalizedLogValues;
break;
default:
valuesSubquery = dbContext.ProcessValues;
break;
}
var res = dbContext.Rooms.Select(r => new
{
RoomId = r.Id,
ZoneId = r.ZoneId,
IdealSetpoint = r.Group.Setpoints.First(sp => sp.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId).Setpoint,
Devices = r.Devices
.Select(rd => rd.Device)
.Select(d => new
{
Id = d.Id,
Name = d.Name,
//Setpoint = GetQuery(rd.Device.Id).Average(t=>t.Value)
Setpoint = valuesSubquery.Where(GetQuery(req.Period, d)).Average(t => t.Value)
})
}
).ToList();
Then I have a function that deals with the predicate dynamically:
private Expression<Func<ProcessValueBase,bool>> GetQuery(string period, DeviceModel device)
{
var predicate = PredicateBuilder.New<ProcessValueBase>();
predicate = predicate.And(v => v.TagSettings.DeviceId == device.Id);
predicate = predicate.And(v => v.TagSettings.TagTypeId == GetSetpointTagTypeId(device.DeviceTypeId));
predicate = predicate.And(v => v.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId);
var utcTime = DateTime.Now.ToUniversalTime();
switch (period)
{
case TimePeriodType.Day:
var startOfDay = utcTime.StartOfDay();
predicate = predicate.And(v => v.Timestamp >= startOfDay && v.Timestamp < startOfDay.AddDays(1));
break;
case TimePeriodType.Week:
var startOfWeek = utcTime.FirstDayOfWeek();
predicate = predicate.And(v => v.Timestamp >= startOfWeek && v.Timestamp < startOfWeek.AddDays(7));
break;
case TimePeriodType.Month:
var startOfMonth = utcTime.FirstDayOfMonth();
predicate = predicate.And(v => v.Timestamp >= startOfMonth && v.Timestamp < startOfMonth.AddMonths(1));
break;
default:
break;
}
return predicate;
}
The error that I'm getting is as follows:
The message is:
Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpression2' to type 'System.Linq.Expressions.LambdaExpression'.
Can anyone suggest what am I doing wrong?
Thanks in advance,
Julian
EDIT:
As an answer to Svyatoslav Danyliv answer. The exception that it gives me is:
The LINQ expression 'DbSet<NormalizedLogValueModel>()
.Where(n => (v, device) => v.TagSettings.DeviceId == device.Id && v.TagSettings.TagTypeId == SetpointSideViewHandler.GetSetpointTagTypeId(device.DeviceTypeId) && v.ClimaticZoneId == DbSet<ClimaticZoneLogModel>()
.OrderByDescending(c0 => c0.Timestamp)
.Select(c0 => c0.ClimaticZoneId)
.First() && v.Timestamp >= __startOfDay_0 && v.Timestamp < __AddDays_1
.Invoke(
arg1: n,
arg2: EntityShaperExpression:
EntityType: DeviceModel
ValueBufferExpression:
ProjectionBindingExpression: Inner
IsNullable: True
))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
When GetQuery(req.Period).Compile()(p,d):
The LINQ expression 'DbSet<NormalizedLogValueModel>()
.Where(n => Invoke(__Compile_0, n, [RelationalEntityShaperExpression])
)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Well answer is complicated, but from my experience it is only one way to do that:
Usage:
var res = dbContext.Rooms
.Select(r => new
{
RoomId = r.Id,
ZoneId = r.ZoneId,
IdealSetpoint = r.Group.Setpoints.First(sp => sp.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId).Setpoint,
Devices = r.Devices
.Select(rd => rd.Device)
.Select(d => new
{
Id = d.Id,
Name = d.Name,
Setpoint = valuesSubquery.Where(p => GetQuery(req.Period).Invoke(dbContext, p, d)).Average(t => t.Value)
})
}
).ToList();
New function (it could be compiler errors, not tested):
private static Expression<Func<DBIContext, ProcessValueBase, DeviceModel, bool>> GetQuery(string period)
{
Expression<Func<DBIContext, ProcessValueBase, DeviceModel, bool>> predicateTemplate = (dbContext, v, device) =>
v.TagSettings.DeviceId == device.Id &&
v.TagSettings.TagTypeId == GetSetpointTagTypeId(device.DeviceTypeId) &&
v.ClimaticZoneId == dbContext.ClimaticZonesLogs.OrderByDescending(cz => cz.Timestamp).First().ClimaticZoneId;
var predicate = PredicateBuilder.New<ProcessValueBase>();
var utcTime = DateTime.Now.ToUniversalTime();
switch (period)
{
case TimePeriodType.Day:
var startOfDay = utcTime.StartOfDay();
predicate = predicate.And(v => v.Timestamp >= startOfDay && v.Timestamp < startOfDay.AddDays(1));
break;
case TimePeriodType.Week:
var startOfWeek = utcTime.FirstDayOfWeek();
predicate = predicate.And(v => v.Timestamp >= startOfWeek && v.Timestamp < startOfWeek.AddDays(7));
break;
case TimePeriodType.Month:
var startOfMonth = utcTime.FirstDayOfMonth();
predicate = predicate.And(v => v.Timestamp >= startOfMonth && v.Timestamp < startOfMonth.AddMonths(1));
break;
default:
break;
}
Expression<Func<ProcessValueBase, bool>> lambda = predicate;
var newBody = predicateTemplate.Body;
newBody = Expression.AndAlso(newBody, ExpressionReplacer.GetBody(lambda, predicateTemplate.Parameters[1]));
return Expression.Lambda<Func<ProcessValueBase, DeviceModel, bool>>(newBody, predicateTemplate.Parameters);
}
[Expandable(nameof(GetSetpointTagTypeIdImpl))]
private static long GetSetpointTagTypeId(long deviceTypeId)
{
throw new NotImplementedException();
}
private static Expression<Func<long, long>> GetSetpointTagTypeIdImpl()
{
return deviceTypeId => deviceTypeId == 180_000 ? 180_002
: deviceTypeId == 190_000 ? 190_002 : 0;
}
And helper class:
public class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> {{toReplace, toExpr}}).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}

Linq to SQL C#: items between dates

I have 2 tables in my SQL server 2012:
Errors (id, cityID, centerID, date)
InspectionVisits (id, cityID, centerID, datePerformed)
I am trying to get the number of errors between inspection visits to see if there is an improvement in the center with the specific centerID and build a chart.
This is my code so far but I can't find out how I can write the where clause to get the number of errors between these inspection visits:
var errorsPerIV = from e in dc.Errors
where e.cityID == ctid && e.centerID == centerid
group e by e.date.Date into g
join iv in dc.InspectionVisits on g.FirstOrDefault().cityID equals iv.cityID
where iv.centerID == g.FirstOrDefault().centerID
select new
{
Day = g.Key.Day + "/" +
g.Key.Month + "/" +
g.Key.Year,
Errors = g.Count()
};
Sample Case
Something like: 5 errors between Inspection_Visit_1 and Inspection_Visit_2, 2 errors between Inspection_Visit_2 and Inspection_Visit_3 and 1 error between Inspection_Visit_3 and today.
EDIT
Maybe it could work if I show queries per day and mark only the inspection visits in the chart's x axis.
I'm not sure if there is a better way, but you could do something like the following
Suppose you have a class like
public class Summary
{
public DateTime? PreviousInspection;
public DateTime? NextInspection;
public int Errors;
}
then you can get most of the information by having a query like
var errorsPerIV = (from e in dc.Errors
where e.cityID == ctid && e.centreID == centreid
// Find the date of the previous inspection (if any)
let previousInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed <= e.date orderby i.datePerformed descending select i.datePerformed).FirstOrDefault()
// Find the date of the next inspection (if any)
let nextInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed > e.date orderby i.datePerformed ascending select i.datePerformed).FirstOrDefault()
group e by new { previousInspection , nextInspection } into results
orderby results.Key.previousInspection
select new Summary
{
PreviousInspection = results.Key.previousInspection,
NextInspection = results.Key.nextInspection ,
Errors = results.Count()
})
.ToList();
However if there are no errors between two visits, then these visits will not appear in your list, so you need to find all the visits and see if there missing, ie something like
var inspectionsDates = (from i in InspectionVisits where i.cityID == ctid && i.centreID == centreid orderby i.datePerformed select i.datePerformed).ToList();
for(int i=0; i< inspectionsDates.Count-1; i++)
{
if (!errorsPerIV.Any(a=>a.PreviousInspection == inspectionsDates[i]))
{
errorsPerIV.Add(new Summary() { PreviousInspection = inspectionsDates[i], NextInspection = inspectionsDates[i + 1], Errors = 0});
}
}
I think this would be easiest processed on the client side.
First you want to get the interesting InspectionVisits and order them by date, then convert to an Enumerable to pull them to the client:
var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp).AsEnumerable();
Now using an extension method that processes along the Enumerable to compute a running value (it is called Scan because it is modeled after the APL Scan operator, which is like an Aggregate that returns all the intermediate values):
// TKey combineFn(T prev, T cur)
// TKey lastKeyFn(T cur)
public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, Func<T, T, TResult> combineFn, Func<T, TResult> lastKeyFn) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prev = srce.Current;
while (srce.MoveNext()) {
yield return combineFn(prev, srce.Current);
prev = srce.Current;
}
yield return lastKeyFn(prev);
}
}
}
You can compute the periods for the inspection visits:
var IVPeriods = orderedIVs.Scan((prev, cur) => new { Begin = prev, End = cur }, cur => new { Begin = cur, End = DateTime.Now });
Finally, with the periods you can count the Errors that occurred between each period:
var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });
If you want to process this on the server side, you must join the InspectionVisits table to itself so you can create the periods. I have not tested this with SQL server:
var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp);
var IVPeriods = (from ivb in orderedIVs
from ive in orderedIVs
where ivb < ive
group ive by ivb into iveg
select new { Begin = iveg.Key, End = iveg.OrderBy(iv => iv).First() })
.Concat((from ivb in orderedIVs orderby ivb descending select new { Begin = ivb, End = DateTime.Now }).Take(1));
var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });

How to convert Linq expression to Reflection (To make dynamic LINQ expression)

I have created three classes.
Two classes Data and IntArrayEqualityComparer are below-
public class Data
{
public Dictionary<int[], List<double>> s = new Dictionary<int[], List<double>>(new IntArrayEqualityComparer());
public Data()
{
}
public Data(Dictionary<int[], List<double>> s)
{
this.s = s;
}
public Dictionary<int[], List<double>> S
{
get { return s; }
set { s = value; }
}
}
public class IntArrayEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
if (x.Length != y.Length)
{
return false;
}
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i])
{
return false;
}
}
return true;
}
public int GetHashCode(int[] obj)
{
int result = 17;
for (int i = 0; i < obj.Length; i++)
{
unchecked
{
result = result * 23 + obj[i];
}
}
return result;
}
}
A third class named Expression is created in which I need to convert LINQ expression into Reflection -
public class Expresion
{
public void CreateExpression()
{
Expression<Func<Data, List<int>>> exp1 = null;
//Below is the LINQ expression I want to convert
exp1 = p2 => p2.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).Select(x => x.index).ToList();
ParameterExpression p1 = Expression.Parameter(typeof(Data), "p");
MethodInfo mInfo = typeof(List<double>).GetMethod("get_Item");
MethodInfo mInfo1 = typeof(Dictionary<int, List<double>>).GetMethod("get_Item");
MethodInfo mInfo2 = typeof(Dictionary<int[], List<double>>).GetMethod("get_Item");
MethodInfo mInfo3 = typeof(List<int[]>).GetMethod("get_Item");
MemberExpression s1 = Expression.Property(p1, "s");
ParameterExpression index1 = Expression.Parameter(typeof(int), "index");
ParameterExpression item1 = Expression.Parameter(typeof(double), "item");
//Here I want to covert the "(item, index) => new { item, index }" part from LINQ expression into Reflection
}
}
Probably the most complex and useless Expression tree I've ever built by hand. Comments inline.
public class Expresion {
// We need the anonymous type that we want to use
private static readonly Type AnonymousType = new { item = 0.0, index = 0 }.GetType();
public void CreateExpression() {
//Below is the LINQ expression I want to convert
Expression<Func<Data, List<int>>> exp2 = p => p.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).Select(x => x.index).ToList();
ParameterExpression p1 = Expression.Parameter(typeof(Data), "p");
MemberExpression s1 = Expression.PropertyOrField(p1, "s");
// The indexer
PropertyInfo dictItem = s1.Type.GetProperty("Item");
// The key to the dictionary, new int[] { 14, 5 }
var key = Expression.NewArrayInit(typeof(int), Expression.Constant(14), Expression.Constant(5));
// s[new int[] { 14, 5 }]
var getItem = Expression.Property(s1, dictItem, key);
// Enumerable.Select with indexer (generic)
var enumerableSelectIndexTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "Select" && x.IsGenericMethod
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,,>).MakeGenericType(args[0], typeof(int), args[1])
select x).Single();
// Enumerable.Select with indexer (non-generic)
var enumerableSelectIndex = enumerableSelectIndexTSourceTResult.MakeGenericMethod(typeof(double), AnonymousType);
// Inner function start
ParameterExpression item1 = Expression.Parameter(typeof(double), "item");
ParameterExpression index1 = Expression.Parameter(typeof(int), "index");
var innerExpression1 = Expression.Lambda(Expression.New(AnonymousType.GetConstructors().Single(), item1, index1), item1, index1);
// .Select((item, index) => new { item, index })
var select1 = Expression.Call(enumerableSelectIndex, getItem, innerExpression1);
// Inner function end
// Enumerable.Select without indexer (generic)
var enumerableSelectTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "Select" && x.IsGenericMethod
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
select x).Single();
// Enumerable.Select without indexer (non-generic)
var enumerableSelect = enumerableSelectTSourceTResult.MakeGenericMethod(AnonymousType, typeof(int));
// Inner function start
ParameterExpression anonymousType1 = Expression.Parameter(AnonymousType, "x");
var innerExpression2 = Expression.Lambda(Expression.Property(anonymousType1, "index"), anonymousType1);
// Inner function end
// .Select((previous select), x => x.index)
var select2 = Expression.Call(enumerableSelect, select1, innerExpression2);
// Enumerable.ToList (generic)
var enumerableToListTSource = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "ToList" && x.IsGenericMethod
let args = x.GetGenericArguments()
where args.Length == 1
let pars = x.GetParameters()
where pars.Length == 1 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0])
select x).Single();
// Enumerable.ToList (non-generic)
var enumerableToList = enumerableToListTSource.MakeGenericMethod(typeof(int));
// .ToList((previous select))
var toList1 = Expression.Call(enumerableToList, select2);
var exp1 = Expression.Lambda<Func<Data, List<int>>>(toList1, p1);
var func1 = exp1.Compile();
// Test
var data = new Data();
data.S[new int[] { 14, 5 }] = new List<double> { 1.0, 2.0 };
var result = func1(data);
}
}
Note that there are some limitations: the anonymous type used must be known at compile time. Using a Tuple<> is often an alternative. In the code the Type AnonymousType line makes the compiler know the type and gets it (through the final .GetType()).
Another important part is the one about finding functions in the Enumerable class. The Select especially is quite complex to find, because there are two different Select with the same number of parameters.

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

How to build up a dynamic OR query in LINQ?

I'm trying to dynamically create a where clause with link.
Now, creating something like SELECT Product WHERE x = 123 AND y = 349 AND w = 948 is no problem. I'm just adding extra Where filters.
products = products.Where(product => product.Property == 123);
But now i have an unknown number of values that should be tested with an OR operator. The SQL Should be like SELECT Product WHERE x = 123 OR x = 499 OR x = 998 .. and n more OR's
Build a list of numbers to check and do something like the following:
var idsToCheck = new List<int>{123, 789, 654}; //Build from dynamic list
products = products.Where(x => idsToCheck.Contains(x.Id));
The above can be duplicated for v and w if required
var idsToCheck = new List<int>{123, 789, 654}; //Build from dynamic list
products = products.Where(x => idsToCheck.Contains(x.Id));
var propertyToCheck = new List<int>{123, 789, 654};//Build from dynamic list
products = products.Where(x => propertyToCheck.Contains(x.Property));
var numberToCheck = new List<int>{123, 789, 654};
products = products.Where(x => numberToCheck.Contains(x.Number));
If your values to check are already being passed in as enumerables then you don't need to build your xxxToCheck collections. You can simply do the .Contains against the originals
You can use contains on a list or just have the equation like so:
products.Where(product => (product.Property == 123) || (product.Property == 499) || (product.Property == 998));
If you want to compose an OR query from multiple where clauses you really need to compose the predicates in the where clauses instead. Afterall, chaining where clauses would be an AND.
So, if you have the following predicates:
Func<Product, bool> w1 = p => p.x == 123;
Func<Product, bool> w2 = p => p.y == 349;
Func<Product, bool> w3 = p => p.w == 948;
Func<Product, bool> w4 = p => p.Property == 123;
Func<Product, bool> w5 = p => p.x == 499;
Func<Product, bool> w6 = p => p.x == 998;
You can define the following extension method to OR them:
public static Func<T, bool> Or<T>(this Func<T, bool> #this, Func<T, bool> that)
{
return t => #this(t) || that(t);
}
Now you could write:
var products1 = products.Where(w1.Or(w5).Or(w6));
var products2 = products.Where(
((Func<Product, bool>)(p => p.x == 123))
.Or(p => p.y == 349)
.Or(p => p.Property == 123));
If you want to work with lists of clauses then add this extension method:
public static Func<T, bool> Or<T>(this IEnumerable<Func<T, bool>> #this)
{
return #this.Aggregate((Func<T, bool>)(t => true), (a, t) => a.Or(t));
}
Now you can write:
var predicates = new List<Func<Product, bool>>()
{
p => p.x == 123,
p => p.w == 948,
p => p.Property == 123,
p => p.x == 998,
};
var products3 = products.Where(predicates.Or());
If you're writing this against an IQueryable<T> provider then you need to work with Expression<Func<Product, bool>> and not just Func<Product, bool>.
Then you need these extension methods:
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> #this,
Expression<Func<T, bool>> that)
{
var param =
Expression
.Parameter(typeof(T), "t");
var body =
Expression
.Or(
Expression
.Invoke(#this, param),
Expression
.Invoke(that, param));
return
Expression
.Lambda<Func<T, bool>>(body, param);
}
public static Expression<Func<T, bool>> Or<T>(
this IEnumerable<Expression<Func<T, bool>>> #this)
{
return #this.Aggregate((Expression<Func<T, bool>>)(t => true), (a, t) => a.Or(t));
}
The calling code still works the same:
var predicatesX = new List<Expression<Func<Product, bool>>>()
{
p => p.x == 123,
p => p.w == 948,
p => p.Property == 123,
p => p.x == 998,
};
var products3X = products.Where(predicatesX.Or());
Is this what you were looking for?

Categories