Filter entities, then sort on dynamic field
I'm trying to FIRST filter a set of products, then order them
(repository.GetAllProducts()
.Where(x=>x.Naam.Contains("filter")) as ObjectQuery<Product>)
.FunkyOrder<Product>(sortfield,sortdirection)
Using the Extension method:
public static IEnumerable<T> FunkyOrder<T>(this ObjectQuery<T> input, string fieldname = "Id", string sortdirection = "asc")
{
swit开发者_StackOverflow中文版ch (sortdirection)
{
case "dsc":
case "desc":
return input.OrderBy("it." + fieldname + " DESC");
default:
return input.OrderBy("it." + fieldname);
}
}
(the GetAllProducts() of my Repository returns an IEnumerable
This FunkyOrder method should be working but that - as ObjectQuery - cast returns NULL
I can only use the FunkyOrder with ObjectQuery because that seems to be the only thing that supports those "it.FieldName" queries.
If I change the - as - notation to A regular cast I get:
Unable to cast object of type 'WhereEnumerableIterator`1[MySecretNameSpace.Product]' to type 'System.Data.Objects.ObjectQuery`1[MySecretNameSpace.Product]'.
Is there any way to make this work or will I be forced to either live with sorting before filtering, or write a giant switch with lambda expressions for my sorting?
This actually turned out to be a GIANT headache to try to solve; but in the end, it actually turned out being very easy to accomplish with a bit of hackishness and reflection:
public class Program
{
public class SomeClass
{
public int Id { get; set; }
public string Name { get; set; }
}
static void Main(string[] args)
{
List<SomeClass> sample = new List<SomeClass>
{
new SomeClass { Id = 4, Name = "ABC" },
new SomeClass { Id = 1, Name = "XYZ" },
new SomeClass { Id = 2, Name = "JKL" }
};
var result = sample.OrderByDynamic("Name", OrderDirection.Ascending).ToList();
result.ForEach(x => Console.WriteLine("Id: " + x.Id + " | Name: " + x.Name));
Console.ReadKey();
}
}
public enum OrderDirection
{
Ascending,
Descending
}
public static class LinqExtensions
{
public static IEnumerable<T> OrderByDynamic<T>(this IEnumerable<T> source, string propertyName, OrderDirection direction = OrderDirection.Ascending)
{
if(direction == OrderDirection.Ascending)
return source.OrderBy(x => x.GetType().GetProperty(propertyName).GetValue(x, null));
else
return source.OrderByDescending(x => x.GetType().GetProperty(propertyName).GetValue(x, null));
}
}
This example worked for me in test situations; I could supply the property name, and specify a sort direction, and it worked great. Simply call like so:
yourEnumerable.OrderByDynamic("YourPropertyName");
yourEnumerable.OrderByDynamic("YourPropertyName", OrderDirection.Descending);
Enjoy!
I would change your FunkyOrder method to IEnumerable and implement your dynamic sorting by creating an Expression dynamically like so
public static IEnumerable<T> FunkyOrder<T, TResult>(this IEnumerable<T> input, string fieldname = "Id", string sortdirection = "asc")
{
ParameterExpresssion parameter = Expression.Parameter(typeof(T), "p");
Expression property = Expression.PropertyOrField(parameter, fieldname);
var lambda = Expression.Lambda<Func<T, TResult>>(property, parameter)
if(sortdirection == "asc")
{
return input.OrderBy(lambda.Compile());
}
else
{
return input.OrderByDescending(lambda.Complile());
}
}
You will no longer need to cast to ObjectQuery
You can use Dynamic Linq for dynamic filtering and sorting. This small library (only one single source file) contains extension methods of IQueryable<T>
like:
public static IQueryable<T> Where<T>(this IQueryable<T> source,
string predicate, params object[] values)
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source,
string ordering, params object[] values)
//...and more like GroupBy, Select
Your query on IEnumerable could then be written as:
repository.GetAllProducts().AsQueryable()
.Where(x => x.Naam.Contains("filter"))
.OrderBy("FieldName desc")
The library implements all the necessary reflection code to perform those dynamic queries.
精彩评论