I have the following expresssion for sorting:
this.Students = this.Students.OrderBy(x => x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault().TotalMarks);
While my idea is to abstract the Expression for
x => x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(
p => p.ExamId).FirstOrDefault().TotalMarks
to be made as an lambda Expression so that I can use like
this.Students = this.Students.OrderBy(sortExpression);
It's because I have many sort fields like the TotalMarks defined above, and I want to just create the Expression from the sort field and then call the OrderBy.
I know from this link, we can create an expression where child property is used, but not getting with the inner expressions.
Currently I have given a switch case and written the same stuff in each case like
this.Students = this.Students.OrderBy(x => x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault().SubjectName);
So my idea is to create kindof ExpressionBuilder with a static method which builds the expression passing on the fieldName, like
public static Expression BuildSortExpression(string fieldName) {}
You can easily pull most of the logic into a method:
private int sortExpression(Student x) {
return x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault().TotalMarks;
}
Assuming TotalMarks is an int.
Then you would just need to use:
this.Students.OrderBy(x => sortExpression(x));
or add it as a property of Student.
Warning:
If you are using this with an ORM (linq to SQL, Entity framework, etc), this will not execute as efficiently as the previous code!
Trying to create a reusable expression variable will end up being a lot more work than just creating your own extension method to do the whole ordering:
public static IQueryable<Student> OrderByMarks(this IQueryable<Student> students)
{
return students.OrderBy(student => student.ExamData
.OrderByDescending(exam => exam.ExamDate)
.ThenBy(exam => exam.ExamId)
.FirstOrDefault().TotalMarks);
}
Then you can use it like so:
this.Students = this.Students.OrderByMarks();
Got a solution for this using the idea by Ben.
Created a
Dictionary<string, Func<Student, Object>>
with the sort fields as key and the func as
new Func<Student, object>((Student student) => { return GetLatestExam(student).TotalMarks; })
And the GetLatestExam static method as
private static Study GetLatestExam(Student student)
{
return student.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault();
}
Then in the actual sort, I just have to call like this:
public void Sort(string sortField, bool sortAscending)
{
// based on the sort field, get the sorting expression and execute.
if(sortAscending)
{
this.Students= this.Students.OrderBy(student=>this._customSortColumns[sortField](student));
}
else
{
this.Patients = this.Patients.OrderByDescending(student=>this._customSortColumns[sortField](student));
}
}
Related
I'm using EntityFrameworkCore and am trying to create a simplified instance of searching for either 'equal to' or 'like' based whether the search object contians the wildcard character.
Here's the base of what I'm working with
public class Person
{
public string Name;
public string MothersName;
public string FathersName;
}
public class SearchPerson
{
public string Name;
}
public class Program
{
public void FindPerson(SearchPerson searchPerson)
{
if (!string.IsNullOrEmpty(searchPerson.Name))
{
if (searchPerson.Name.Contains("%"))
{
EFPersonObject.Where(m => EF.Functions.Like(m.Name, searchPerson.Name));
}
else
{
EFPersonObject.Where(m => m.Name == searchPerson.Name);
}
}
}
}
If my SearchPerson class extends to 5 or 10 or 15 possible search params, there is a lot of repeated code. I should be able to implement some reflection in an extension and using Jim C's response here, get and pass the name of the property and simplify a lot of it down to one line
public static class SearchExtension
{
public static void FindLike<T>(this DbSet<T> model, PropertyInfo info, string searchValue) where T : class
{
if (!string.IsNullOrEmpty(searchValue))
{
if (searchValue.Contains("%"))
{
model.Where(m => EF.Functions.Like(typeof(T).GetProperty(info.Name).GetValue(model, null).ToString(), searchValue));
}
else
{
model.Where(m => typeof(T).GetProperty(info.Name).GetValue(model, null).ToString() == searchValue);
}
}
}
}
Usage:
EFPersonObject.FindLike(typeof(Person).GetProperty(RemoteMgr.GetPropertyName(()=>typeof(Person).Name)), searchPerson.Name);
(I haven't tested it yet, but if it isn't right, it should be close), but I'm going to assume I'm going to take a performance hit. Is there another way to implement this where reflection isn't needed to avoid the performance hit?
Using reflection (and other non SQL translatable) calls inside the query expression tree is not a good idea. In EF Core 1x and 2.x it will cause client evaluation, and EF Core v3+ will throw exception similar to EF 6.
LINQ to Entities best work with expressions. And once you need expression, you'd better make your custom extension method receive lambda expression directly rather than PropertyInfo obtained via lambda expression as in the linked topic.
Here is a sample implementation of the above:
public static partial class QueryableExtensions
{
public static IQueryable<T> WhereMatch<T>(this IQueryable<T> source, Expression<Func<T, string>> expr, string searchValue)
{
if (string.IsNullOrEmpty(searchValue))
return source;
else if (searchValue.Contains("%"))
return source.Where(expr.Map(value => EF.Functions.Like(value, searchValue)));
else
return source.Where(expr.Map(value => value == searchValue));
}
static Expression<Func<TSource, TTarget>> Map<TSource, TIntermediate, TTarget>(this Expression<Func<TSource, TIntermediate>> source, Expression<Func<TIntermediate, TTarget>> target)
=> Expression.Lambda<Func<TSource, TTarget>>(Expression.Invoke(target, source.Body), source.Parameters);
}
The main method is WhereMatch. It uses a small Expression helper method called Map for composing lambda expressions from other lambda expressions.
Sample usage would be:
// SearchPerson searchPerson
// DbContext db
var query = db.Set<Person>()
.WhereMatch(p => p.Name, searchPerson.Name)
.WhereMatch(p => p.MothersName, searchPerson.MothersName)
.WhereMatch(p => p.FathersName, searchPerson.FathersName);
For Equality comparison you should use ==:
EFPersonObject.Where(m => m.Name == searchPerson.Name);
For LIKE :
like 'something%': (StartsWith Method)
EFPersonObject.Where(m => m.Name.StartsWith(searchPerson.Name));
like '%something': (EndsWith Method)
EFPersonObject.Where(m => m.Name.EndsWith(searchPerson.Name));
like '%something%': (Contains Method)
EFPersonObject.Where(m => m.Name.Contains(searchPerson.Name));
I've been using LinqKit to create generic queries for quite some time.
One thing that has always bothered me is the fact that you always have to test whether the value sent in the filter is valid.
For example: Suppose I have a string filter. Conditions can be Equal, StartsWith, EndsWith and Contains.
My method would look something like this:
public List<MyModel> Get(MyModelFilter filter)
{
if (string.IsNullOrEmpty(filter.prop))
{
predicate = predicate.And(_myModel => myModel.Prop.Contains(filter.prop));
}
// Plus a giant amount of if's with multiple filters
return DbSet.AsExpandable()
.Where(predicate)
.ToList();
}
To end this bunch of If's, I decided to create a generic method to apply the filter to the properties.
My idea is to pass the property where the filter will be applied, and the filter definition, and encapsulate the Expression creation logic
It would be something of the type:
public List<MyModel> Get(MyModelFilter filter)
{
predicate = predicate.And(_myModel => myModel.Prop, filter.PropFilterDefinition);
// Goodnye If's, Only others filter impl
return DbSet.AsExpandable()
.Where(predicate)
.ToList();
}
For this, I've created some extension methods to handle this
public static Expression<Func<TPredicate, bool>> And<TPredicate>(
this ExpressionStarter<TPredicate> predicate,
Func<TPredicate, string> property, StringFilterDefinition filter,
bool ignoreNull = true)
{
if (InvalidStringFilter(filter, ignoreNull))
{
return predicate;
}
// This is LinqKit's And Extension Method
return predicate.And(BuildPredicate(property, filter));
}
private static Expression<Func<TPredicate, bool>> BuildPredicate<TPredicate>(
Func<TPredicate, string> property,
StringFilterDefinition filter)
{
if (filter.Filter == StringFilterComparators.Equal)
{
return x => property.Invoke(x) == filter.Value;
}
if (filter.Filter == StringFilterComparators.BeginsWith)
{
return x => property.Invoke(x).StartsWith(filter.Value);
}
if (filter.Filter == StringFilterComparators.EndsWith)
{
return x => property.Invoke(x).EndsWith(filter.Value);
}
return x => property.Invoke(x).Contains(filter.Value);
}
private static bool InvalidStringFilter(
StringFilterDefinition filter,
bool ignoreNullValue = true)
{
if (filter?.Filter == null)
{
return true;
}
return ignoreNullValue && string.IsNullOrEmpty(filter.Value);
}
The problem is that the filter is not applied, and the answer is in Invoke right up there. EF can not translate the above expression to SQL.
The EF error is
Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory[8]
The LINQ expression '(__property_0.Invoke([x]) == __filter_Value_1)'
could not be translated and will be evaluated locally. To configure
this warning use the DbContextOptionsBuilder.ConfigureWarnings API
(event id 'RelationalEventId.QueryClientEvaluationWarning').
ConfigureWarnings can be used when overriding the
DbContext.OnConfiguring method or using AddDbContext on the
application service provider.
The question is:
How can I make this construction work?
Also, any suggestions on how best this?
You seem to forgot that besides the PredicateBuilder, the really useful feature provided by LINQKit AsExpandable, Expand and Invoke custom extension methods is to be able to correctly embed expressions inside the expression tree.
In order to utilize that feature, you should use Expression<Func<...>> instead of Func<...>. In the posted code, replace all occurrences of Func<TPredicate, string> with Expression<Func<TPredicate, string>> and the issue should be solved.
I'm trying to dynamically construct an expression similar to the one below, where I can use the same comparison function, but where the values being compared can be passed in, since the value is passed from a property 'higher-up' in the query.
var people = People
.Where(p => p.Cars
.Any(c => c.Colour == p.FavouriteColour));
I believe I've constructed the query correctly, but the ExpressionExpander.VisitMethodCall(..) method throws the following exception when I try to use it:
"Unable to cast object of type 'System.Linq.Expressions.InstanceMethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'"
In real-world code, using Entity Framework and actual IQueryable<T>, I often get:
"Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.LambdaExpression'" as well.
I've constructed a LinqPad-friendly example of my problem, as simple as I could make it.
void Main()
{
var tuples = new List<Tuple<String, int>>() {
new Tuple<String, int>("Hello", 4),
new Tuple<String, int>("World", 2),
new Tuple<String, int>("Cheese", 20)
};
var queryableTuples = tuples.AsQueryable();
// For this example, I want to check which of these strings are longer than their accompanying number.
// The expression I want to build needs to use one of the values of the item (the int) in order to construct the expression.
// Basically just want to construct this:
// .Where (x => x.Item1.Length > x.Item2)
var expressionToCheckTuple = BuildExpressionToCheckTuple();
var result = queryableTuples
.AsExpandable()
.Where (t => expressionToCheckTuple.Invoke(t))
.ToList();
}
public Expression<Func<string, bool>> BuildExpressionToCheckStringLength(int minLength) {
return str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
return tuple => BuildExpressionToCheckStringLength(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
If I'm doing something obviously wrong, I'd really appreciate a nudge in the right direction! Thanks.
Edit: I know that the following would work:
Expression<Func<Tuple<string, int>, bool>> expr = x => x.Item1.Length > x.Item2;
var result = queryableTuples
.AsExpandable()
.Where (t => expr.Invoke(t))
.ToList();
However, I'm trying to separate the comparison from the location of the parameters, since the comparison could be complex and I would like to re-use it for many different queries (each with different locations for the two parameters). It is also intended that one of the parameters (in the example, the 'min length') would actually be calculated via another expression.
Edit: Sorry, I've just realised that some answers will work when attempted against my example code since my example is merely masquerading as an IQueryable<T> but is still a List<T> underneath. The reason I'm using LinqKit in the first place is because an actual IQueryable<T> from an EntityFramework DbContext will invoke Linq-to-SQL and so must be able to be parsed by Linq-to-SQL itself. LinqKit enables this by expanding everything to expressions.
Solution! Thanks to Jean's answer below, I think I've realised where I'm going wrong.
If a value has come from somewhere in the query (i.e. not a value that is known before-hand.) then you must build the reference/expression/variable to it into the expression.
In my original example, I was trying to pass the 'minLength' value taken from within the expression and pass it to a method. That method call could not be done before-hand, since it used a value from the expression, and it could not be done within the expression, since you can't build an expression within an expression.
So, how to get around this? I chose to write my expressions so that they can be invoked with the additional parameters. Though this has the downside that the parameters are no longer 'named' and I could end up with an Expression<Func<int, int, int, int, bool>> or something down the line.
// New signature.
public Expression<Func<string, int, bool>> BuildExpressionToCheckStringLength() {
// Now takes two parameters.
return (str, minLength) => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// Construct the expression before-hand.
var expression = BuildExpressionToCheckStringLength();
// Invoke the expression using both values.
return tuple => expression.Invoke(tuple.Item1 /* string */, tuple.Item2 /* the length */);
}
OK, so what you are trying to do (the transformation from a function that takes a single argument, that returns another function that takes a single argument f(x)(y) into a function that takes two arguments f(x, y)) is known as uncurrying. Look it up! :)
Now, the issue that you have in your code is that, in the expression returned by BuildExpressionToCheckTuple, there is a method call to BuildExpressionToCheckStringLength, which is not resolved. And you cannot resolve it because it takes an argument that is embedded in the tuple parameter.
The solution is, instead of using a method call, to use a lambda expression that will be equivalent to that method call.
That is:
public Expression<Func<int, Func<string, bool>>> ExpressionToCheckStringLengthBuilder() {
return minLength =>
str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
// Putting builder into a variable so that the resulting expression will be
// visible to tools that analyze the expression.
var builder = ExpressionToCheckStringLengthBuilder();
return tuple => builder.Invoke(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
So you are looking for something like this:
public static class Program
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static IQueryable<T> WherePropertyEquals<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
{
var result = src.Where(e => property.Invoke(e).Equals(value));
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => property.Invoke(e).CompareTo(value) > 0);
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> left, Expression<Func<T, TProperty>> right)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => left.Invoke(e).CompareTo(right.Invoke(e)) > 0);
return result;
}
public static void Main()
{
var persons = new List<Person>()
{
new Person
{
FirstName = "Jhon",
LastName = "Smith"
},
new Person
{
FirstName = "Chuck",
LastName = "Norris"
},
new Person
{
FirstName = "Ben",
LastName = "Jenkinson"
},
new Person
{
FirstName = "Barack",
LastName = "Obama"
}
}
.AsQueryable()
.AsExpandable();
var chuck = persons.WherePropertyEquals(p => p.FirstName, "Chuck").First();
var ben = persons.WhereGreater(p => p.LastName.Length, 6).First();
var barack = persons.WhereGreater(p => p.FirstName.Length, p => p.LastName.Length).First();
}
I may have function calls like this :
foo(**new {x.ID,x.Name}**);
and LINQ:
(IQueryable<SomeTableName>).where(x=>x.ID>1).select(x=>**new {x.ID,x.Name}**);
Is it possible to replace the "new {x.ID,x.Name}" part with a function, expression or variable, so I can change from time to time, in only 1 place?
Thank you.
I know I can make an Expression, which be used inside LINQ only
public static Func<SomeTableName, Object> Select_RS_Food = x => new { x.ID,x.Name };
but I also want to use for normal anonymous object creating. Like:
foo(CreateMyObject(x));
Update
Current:
return new { myTable.ID, myTable.Name};
//And
db.SomeTable.Select (x=> new { myTable.ID, myTable.Name));
Expect:
return SomeMagicCode;
//And
db.SomeTable.Select (x=> SomeMagicCode);
You could define a helper class like this to allow you to specify only some of the generic type parameters when creating a Func<T, R>:
public static class Funk<T>
{
public static Func<T, R> Make<R>(Func<T, R> func)
{
return func;
}
}
And use it like this:
var selector = Funk<SomeTableName>.Make(x => new {x.ID, x.Name});
var result = db.SomeTable.Where(x => x.ID>1).Select(selector);
This takes advantage of type inference to determine the return type, so you only have to define the input type (SomeTableName). The var keyword makes the selector variable implicitly typed, you won't have to specify Func<SomeTableName, _AnonymousType1_>. You can now reuse selector in multiple queries.
You will not be able to save this as property or field of a class, however, because implicit typing is not supported in type members. If you want to be able to reuse this selection function, you must use a named type or perhaps dynamic—but I wouldn't expect this to work with any current ORM.
For the other lambda expressions (x => x.ID == 1 and x => x.ID > 1), there are no anonymous types involved so you can simply do this:
public static Func<SomeTableName, bool> IDEquals1 = x => x.ID == 1;
public static Func<SomeTableName, bool> IDGreaterThan1 = x => x.ID == 1;
But by the way, if your using an ORM like entity framework, you probably need to use an Expression instead. Maybe something kind of like this:
public static Expression<Func<T, bool>> IDEquals1 = x => x.ID == 1;
Or even this:
public interface IHasID
{
int ID { get; }
}
public SomeTableName : IHasID { ... }
public static Expression<Func<T, bool>> IDEquals1<T>() where T : IHasID
{
return x => x.ID == 1;
}
How can I dynamically sort an Entity Framework query based on a value provided by client?
I have 2 user inputs: one that is value to filter the project by and the other is the way to order the results - either by date, state, priority or type.
The data is displayed in a Grid.
I have one query like this:
Expression<Func<Issue, object>> sortExpression = IssuesConversionsFilter.Convert(sortBy);
Requests = query.Where(i => i.Project.ProjectID == projectId && i.Project.Enabled)
.OrderByDescending(sortExpression)
.Skip(( currentPage - 1 )*take)
.Take(take)
IssuesConversionFilter is a static class with a cache keyed by an enum and with a value that is an Expression<Func<Issue, object>>:
internal static class IssuesConversionsFilter
{
readonly static IDictionary<IssuesSortBy, Expression<Func<Issue, object>>> Cache = new Dictionary<IssuesSortBy, Expression<Func<Issue, object>>>();
static IssuesConversionsFilter() {
Cache.Add(IssuesSortBy.Date, i => i.CreatedDate);
Cache.Add(IssuesSortBy.Priority, i => i.Priority);
Cache.Add(IssuesSortBy.Type, i => i.Type);
Cache.Add(IssuesSortBy.State, i => i.State);
}
public static Expression<Func<Issue, object>> Convert(IssuesSortBy sortBy) {
if(Cache.ContainsKey(sortBy) == false)
throw new InvalidOperationException();
return Cache[sortBy];
}
}
The problem that the return type has to be an expression that returns an object and LINQ to Entities only seems to support primitive types. With LING-to-Objects this works fine.
How can I get this to work?
Instead of trying to get the helper class to be able to return a generic expression that can be used in the OrderBy(Descending) method call (which you've found is difficult when the data types aren't all the same), it will be much easier to just perform the call to OrderBy(Descending) in the helper method. For example:
internal static class IssuesConversionsFilter
{
public static IOrderedQueryable<Issue> Convert(IQueryable<Issue> query, IssuesSortBy sortBy)
{
switch (sortBy)
{
case IssuesSortBy.Date:
return query.OrderByDescending(i => i.CreatedDate);
case IssuesSortBy.Priority:
return query.OrderByDescending(i => i.Priority);
case IssuesSortBy.Type:
return query.OrderByDescending(i => i.Type);
case IssuesSortBy.State:
return query.OrderByDescending(i => i.State);
default:
throw new ArgumentOutOfRangeException("sortBy");
}
}
}
Then you can use that method like this:
var orderedQuery = IssuesConversionsFilter.Convert(unOrderedQuery, IssuesSortBy.Date);
You could also change the signature of the Convert method to include this:
public static IOrderedQueryable<Issue> Convert(this IQueryable<Issue> query, IssuesSortBy sortBy)
and then you'd be able to use it as an extension method. That way, you'll maintain the fluid calling style from your example:
var Requests = query.Where(i => i.Project.ProjectID == projectId && i.Project.Enabled)
.Convert(sortBy)
.Skip(( currentPage - 1 )*take)
.Take(take)