I want to be able to add ordering to my queries dynamically:
Expression<Func<IQueryable<MyEntity>, IOrderedQueryable<MyEntity>>> order1 = e => e.OrderBy(x => x.Weight);
Expression<Func<IQueryable<MyEntity>, IOrderedQueryable<MyEntity>>> order2 = e => e.OrderByDescending(x => x.Weight).ThenBy(x => x.Price);
Expression<Func<IQueryable<MyEntity>, IOrderedQueryable<MyEntity>>> order3 = e => e.OrderBy(x => x.Category).ThenBy(x => x.Price);
IQueryable<MyEntity> query = any EF query;
var transformedQuery = query.Transform(order1/order2/order3);
How do I implement Transform() ?
public static IQueryable<T> Transform<T>(this IQueryable<T> query, Expression<Func<IQueryable<T>, IOrderedQueryable<T>>> orderExpr)
{
// ??????????????????
}
My problem is that I don't want to have 2 overloads for ascending/descending ordering. I need to apply whatever OrderBy()/OrderByDescending() expression or their combination is passed.
This is one case where you don't need an expression tree. Or even a helper method.
Func<IQueryable<MyEntity>, IOrderedQueryable<MyEntity>> order = e => e.OrderBy(x => x.Weight);
IQueryable<MyEntity> query = any EF query;
var transformedQuery = order(query);
This works because when order is passed in a query, it can itself call the appropriate Queryable.OrderBy overload that takes an expression tree.
Related
I have the following code:
if(result)
{
var query = people.OrderByDescending(person => person.Name)
.Select(person => person.Name);
}
else {
var query = people.OrderBy(person => person.Name)
.Select(person => person.Name);
}
The only difference between this, is that if result is true, it will OrderByDescending, else OrderBy.
Is there a way to clean this up and have less redundant code?
Use ternary operator, and change the order of the extension methods:
var query = people.Select(person => person.Name);
query = result ? query.OrderByDescending(p => p)
: query.OrderBy(p => p);
If you are asking for a way to actually avoid either calling OrderBy or OrderByDescending based on result, no there is not. If you have this type of code sprinkled all over the place, then I'd advise you create a helper extension method to encapsulate this:
public static IEnumerable<Q> OrderBy<T,Q>(
this IEnumerable<T> source,
Func<T,Q> keySelector,
bool descending)
{
var query = source.Select(keySelector);
return descending ? query.OrderByDescending(p => p)
: query.OrderBy(p => p);
}
I have a long Linq Where clause that I would like to populate with a predicate list.
List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();
filters.Add(p => p.Title != null && p.Title.ToLower().Contains(searchString));
filters.Add(p => p.Notes != null && p.Notes.ToLower().Contains(searchString));
filters.Add(GlobalSearchUser((List < User > users = new List<User>() { p.user1, p.user2, p.user3, p.user4 }), searchString));
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.Where(filters.ToArray()).Take(10).ToList();
However I'm getting this error:
cannot convert from 'System.Linq.Expressions.Expression<System.Func<project.Contracts.DTOs.Note,bool>>[]' to 'System.Func<project.Contracts.DTOs.Note,bool>'
Which is an error on the .where clause. Pulling out the .where compiles just fine.
I think great answer from Hogan can be simplified and shorten a bit by use of Any and All Linq methods.
To get items that fulfill all the conditions:
var resultAll = listOfItems.Where(p => filters.All(f => f(p)));
And to get the items that fulfill any condition:
var resultAny = listOfItems.Where(p => filters.Any(f => f(p)));
There are at least two errors in your code:
List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();
change it to
List<Func<Note, bool>> filters = new List<Func<Note, bool>>();
You don't need Expression trees here. You are using IEnumerable<>, not IQueryable<>
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.Where(filters.ToArray()).Take(10).ToList();
There .Where() accepts a single predicate at a time. You could:
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.Where(x => filters.All(x)).Take(10).ToList();
or various other solutions, like:
var notesEnu = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
.AsEnumerable();
foreach (var filter in filters)
{
notesEmu = notesEmu.Where(filter);
}
notes = notesEnu.Take(10).ToList();
Because all the .Where() conditions are implicitly in &&.
You have to loop over your filters and run a test on each one.
You can do it with linq like this to return true if any of your filters are true:
.Where(p => { foreach(f in filters) if (f(p) == true) return(true); return(false)})
or like this to to return true if all of your filters are true:
.Where(p => { foreach(f in filters) if (f(p) == false) return(false); return(true)})
You can't just pass an array of predicates to the where method. You need to either iterate over the array and keep calling Where() for each expression in the array, or find a way to merge them all together into one expression and use that. You'll want to use LinqKit if you go the second route.
I'm trying to create an expression in order to retrieve a Store object that should be on a list of countries and also that their Store.Id = X.
I'm trying to do that with the following expression, but that returns all Stores stored on the database, I don't know what I'm missing.
public Expression<Func<Store, bool>> CreateExpression(List<Country> countries, long storeId)
{
var predicate = PredicateBuilder.False<Store>();
predicate = countries.Aggregate(predicate, (current, p) =>
current.Or(e => e.Country.Id == p.Id));
predicate = predicate.And(e => e.Id == storeId);
return predicate;
}
I want to pass dynamic lambda expressions to the function below, but I'm not sure how to define the .Take() or .OrderByDescending() on the expression object.
If I want to call the function below, then I want to be able to do this:
dbprovider.Query = (x => x.ConfigurationReference == "172.16.59.175")
.Take(100)
.OrderByDescending(x.Date)
FindEntities(db, dbprovider.Query)
But I can't (this syntax is invalid). Any ideas?
public static List<T> FindEntities<T>(TrackingDataContext dataContext, System.Linq.Expressions.Expression<Func<T, bool>> find) where T : class
{
try
{
var val = dataContext.GetTable<T>().Where(find).ToList<T>();
return val;
}
catch (Exception ex)
{
throw ex;
}
}
The parameter is of type:
System.Linq.Expressions.Expression<Func<T, bool>> find
That means it can take a predicate (the "where" clause), and only a predicate. Thus the only bit you can pass in there is the filter:
x => x.ConfigurationReference == "172.16.59.175"
To do what you want, you would need to add the rest of the code in FindEntities, so that it becomes:
var val = dataContext.GetTable<T>().Where(find)
.OrderByDescending(x => x.Date).Take(100).ToList<T>();
(note also that the Take should really be after the OrderByDescending)
One way you could do that would be:
public static List<T> FindEntities<T>(TrackingDataContext dataContext,
System.Linq.Expressions.Expression<Func<T, bool>> find,
Func<IQueryable<T>, IQueryable<T>> additonalProcessing = null
) where T : class
{
var query = dataContext.GetTable<T>().Where(find);
if(additonalProcessing != null) query = additonalProcessing(query);
return query.ToList<T>();
}
and call:
var data = FindEntities(db, x => x.ConfigurationReference == "172.16.58.175",
q => q.OrderByDescending(x => x.Date).Take(100));
However, frankly I'm not sure what the point of this would be... the caller could do all of that themselves locally more conveniently, without using FindEntities at all. Just:
var data = db.GetTable<T>()
.Where(x => x.ConfigurationReference == "172.16.58.175")
.OrderByDescending(x => x.Date).Take(100).ToList();
or even:
var data = db.SomeTable
.Where(x => x.ConfigurationReference == "172.16.58.175")
.OrderByDescending(x => x.Date).Take(100).ToList();
or just:
var data = (from row in db.SomeTable
where row.ConfigurationReference == "172.16.58.175"
orderby row.Date descending
select row).Take(100).ToList();
I have two tables. Report and ReportData.
ReportData has a constraint ReportID.
How can I write my linq query to return all Report objects where the predicate conditions are met for ReportData? Something like this in SQL:
SELECT * FROM Report as r
Where r.ServiceID = 3 and r.ReportID IN (Select ReportID FROM ReportData WHERE JobID LIKE 'Something%')
This is how I'm building my predicate:
Expression<Func<ReportData, bool>> predicate = PredicateBuilder.True<ReportData>();
predicate = predicate.And(x => x.JobID.StartsWith(QueryConfig.Instance.DataStreamName));
var q = engine.GetReports(predicate, reportsDataContext);
reports = q.ToList();
This is my query construction at the moment:
public override IQueryable<Report> GetReports(Expression<Func<ReportData, bool>> predicate, LLReportsDataContext reportDC)
{
if (reportDC == null) throw new ArgumentNullException("reportDC");
var q = reportDC.ReportDatas.Where(predicate).Where(r => r.ServiceID.Equals(1)).Select(r => r.Report);
return q;
}
I've tried doing the following as well:
public override IQueryable GetReports(Expression> predicate, LLReportsDataContext reportDC)
{
if (reportDC == null) throw new ArgumentNullException("reportDC");
var q = from r in reportDC.Reports
where r.ServiceID.Equals(1)
where r.ReportDatas.Where(predicate.Compile()).Select(x => r.ReportID).Contains(r.ReportID)
select r;
return q;
}
However, I get the this Exception: "Unsupported overload used for query operator 'Where'."
UPDATE
This fixed it:
var q = reportDC.Reports.AsExpandable().
Where(r => r.ReportDatas.Any(predicate.Compile()))
.Where(r => r.ServiceID.Equals(1));
Query
ReportDatas
.Where( reportData => reportData.StartsWith( "Something%" ) &&
reportData.Report.Id ==3)
.Select( reportData => reportData.Report )
.Distinct()
AboutLinqKit
When using LinqKit, sometimes you need to call AsExpandable() in the entity collection and to compile the predicate expression. see this example : ): how-to-use-predicate-builder-with-linq2sql-and-or-operator