I'm building a generic interface to expose selected string properties out of a class, and then I want to search for a text inside every one of those fields, to check if it's a match.
Here's my IFieldExposer interface:
using System;
using System.Collections.Generic;
public interface IFieldExposer<T>
{
IEnumerable<Func<T, string>> GetFields();
}
Now, I implement it like this in my DataClass to expose the properties I would like to iterate. Note that I'm also exposing a property from my ChildClass:
using System;
using System.Collections.Generic;
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> GetFields()
{
return new List<Func<DataClass, string>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
class ChildClass
{
public string PropertyThree { get; set; }
}
I've also created extension methods for IFieldExposer<T> because I want to keep it simple and be able to simply call obj.Match(text, ignoreCase) everywhere else in my code. This method should tell me if my object is a match for my text. Here's the code for the ExtensionClass, which isn't working as expected:
using System;
using System.Linq.Expressions;
using System.Reflection;
public static class ExtensionClass
{
public static bool Match<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
Func<bool> expression = Expression.Lambda<Func<bool>>(obj.CreateExpressionTree(text, ignoreCase)).Compile();
return expression();
}
private static Expression CreateExpressionTree<T>(this IFieldExposer<T> obj, string text, bool ignoreCase)
{
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
var exposedFields = obj.GetFields();
if (ignoreCase)
{
// How should I do convert these to lower too?
// exposedFields = exposedFields.Select(e => e.???.ToLower());
text = text.ToLower();
}
Expression textExp = Expression.Constant(text);
Expression orExpressions = Expression.Constant(false);
foreach (var field in exposedFields)
{
//How should I call the contains method on the string field?
Expression fieldExpression = Expression.Lambda<Func<string>>(Expression.Call(Expression.Constant(obj), field.Method)); //this doesn't work
Expression contains = Expression.Call(fieldExpression, containsMethod, textExp);
orExpressions = Expression.Or(orExpressions, contains);
}
return orExpressions;
}
}
Please check the comments in the code above. I would like to know how to convert all my string properties to lowercase (if desired) and how to call string.Contains in each one of them. I get this error when I create my fieldExpression:
Method 'System.String <GetFields>b__12_0(DataClass)' declared on type 'DataClass+<>c' cannot be called with instance of type 'DataClass'
I don't have experience working with Expression Trees. I've spent hours reading docs and other answers for similar issues but I still can't understand how to achieve what I want... I have no clue what to do now.
I'm testing this in a console app so here's the main class if you want to build it yourself:
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
var data = new DataClass
{
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass
{
PropertyThree = "Dolor"
}
};
var dataList = new List<DataClass> { data };
var results = dataList.Where(d => d.Match("dolor", true));
}
}
EDIT
I forgot to mention that my dataList should be IQueryable and I want to execute my code in SQL, that's why I'm trying to build the expression trees myself. So it appears my example code should be:
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable();
var results = query.Where(ExtensionClass.Match<DataClass>("lorem dolor"));
while my method becomes: (I'm following #sjb-sjb's answer and changed the GetFields() method in IFieldExposer<T> to a SelectedFields property)
public static Expression<Func<T, bool>> Match<T>(string text, bool ignoreCase) where T : IFieldExposer<T>
{
ParameterExpression parameter = Expression.Parameter(typeof(T), "obj");
MemberExpression selectedFieldsExp = Expression.Property(parameter, "SelectedFields");
LambdaExpression lambda = Expression.Lambda(selectedFieldsExp, parameter).Compile();
[...]
}
And then it seems that I have to dinamically call selectedFieldsExp with Expression.Lambda. I came up with:
Expression.Lambda(selectedFieldsExp, parameter).Compile();
and that works, but I don't know how to properly call DynamicInvoke() for the lambda expression.
It throws Parameter count mismatch. if I call it without parameters and Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'DataClass'. if I do DynamicInvoke(parameter).
Any ideas?
Before getting to the implementation, there are some design flaws that needs to be fixed.
First, almost all query providers (except LINQ to Object which simply compiles the lambda expressions to delegates and executes them) don't support invocation expressions and custom (unknown) methods. That's because they do not execute the expressions, but translate them to something else (SQL for instance), and translation is based on pre knowledge.
One example of invocation expression are Func<...> delegates. So the first thing you should do is to use Expression<Func<...>> wherever you currently have Func<...>.
Second, the query expression trees are built statically, i.e. there is no real object instance you can use to obtain the metadata, so the idea of IFieldExposer<T> won't work. You'd need a statically exposed list of expressions like this:
class DataClass //: IFieldExposer<DataClass>
{
// ...
public static IEnumerable<Expression<Func<DataClass, string>>> GetFields()
{
return new List<Expression<Func<DataClass, string>>>
{
a => a.PropertyOne,
b => b.Child.PropertyThree
};
}
}
Then the signature of the method in question could be like this
public static Expression<Func<T, bool>> Match<T>(
this IEnumerable<Expression<Func<T, string>>> fields, string text, bool ignoreCase)
with usage like this
var dataList = new List<DataClass> { data };
var query = dataList.AsQueryable()
.Where(DataClass.GetFields().Match("lorem", true));
Now the implementation. The desired expression could be built purely with Expression class methods, but I'll show you an easier (IMHO) method, which composes expression from compile time expression by replacing the parameter(s) with other expression(s).
All you need is a small helper utility method for replacing lambda expression parameter with another expression:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : base.VisitParameter(node);
}
}
Internally it uses ExpressionVistor to find each instance of the passed ParameterExpression and replace it with the passed Expression.
With this helper method, the implementation could be like this:
public static partial class ExpressionUtils
{
public static Expression<Func<T, bool>> Match<T>(this IEnumerable<Expression<Func<T, string>>> fields, string text, bool ignoreCase)
{
Expression<Func<string, bool>> match;
if (ignoreCase)
{
text = text.ToLower();
match = input => input.ToLower().Contains(text);
}
else
{
match = input => input.Contains(text);
}
// T source =>
var parameter = Expression.Parameter(typeof(T), "source");
Expression anyMatch = null;
foreach (var field in fields)
{
// a.PropertyOne --> source.PropertyOne
// b.Child.PropertyThree --> source.Child.PropertyThree
var fieldAccess = field.Body.ReplaceParameter(field.Parameters[0], parameter);
// input --> source.PropertyOne
// input --> source.Child.PropertyThree
var fieldMatch = match.Body.ReplaceParameter(match.Parameters[0], fieldAccess);
// matchA || matchB
anyMatch = anyMatch == null ? fieldMatch : Expression.OrElse(anyMatch, fieldMatch);
}
if (anyMatch == null) anyMatch = Expression.Constant(false);
return Expression.Lambda<Func<T, bool>>(anyMatch, parameter);
}
}
The input => input.ToLower().Contains(text) or input => input.Contains(text) is our compile time match expression, which we then replace the input parameter with the body of the passed Expression<Func<T, string>> lambda expressions, with their parameter replaced with a common parameter used in the final expression. The resulting bool expressions are combined with Expression.OrElse which is the equivalent of the C# || operator (while Expression.Or is for bitwise | operator and in general should not be used with logical operations). Same btw for && - use Expression.AndAlso and not Expression.And which is for bitwise &.
This process is pretty much the expression equivalent of the string.Replace. In case the explanations and code comments are not enough, you can step through the code and see the exact expression transformations and expression building process.
There is no need to get into the complexities of dynamically creating an Expression, because you can just invoke the Func delegate directly:
public interface IFieldExposer<T>
{
IEnumerable<Func<T,string>> SelectedFields { get; }
}
public static class FieldExposerExtensions
{
public static IEnumerable<Func<T,string>> MatchIgnoreCase<T>( this IEnumerable<Func<T,string>> stringProperties, T source, string matchText)
{
return stringProperties.Where(stringProperty => String.Equals( stringProperty( source), matchText, StringComparison.OrdinalIgnoreCase));
}
}
class DataClass : IFieldExposer<DataClass>
{
public string PropertyOne { get; set; }
public string PropertyTwo { get; set; }
public ChildClass Child { get; set; }
public IEnumerable<Func<DataClass, string>> SelectedFields {
get {
return new Func<DataClass, string>[] { #this => #this.PropertyOne, #this => #this.Child.PropertyThree };
}
}
public override string ToString() => this.PropertyOne + " " + this.PropertyTwo + " " + this.Child.PropertyThree;
}
class ChildClass
{
public string PropertyThree { get; set; }
}
Then to use it,
class Program
{
static void Main(string[] args)
{
var data = new DataClass {
PropertyOne = "Lorem",
PropertyTwo = "Ipsum",
Child = new ChildClass {
PropertyThree = "Dolor"
}
};
var data2 = new DataClass {
PropertyOne = "lorem",
PropertyTwo = "ipsum",
Child = new ChildClass {
PropertyThree = "doloreusement"
}
};
var dataList = new List<DataClass>() { data, data2 };
IEnumerable<DataClass> results = dataList.Where( d => d.SelectedFields.MatchIgnoreCase( d, "lorem").Any());
foreach (DataClass source in results) {
Console.WriteLine(source.ToString());
}
Console.ReadKey();
}
}
Following up on my comment above, I think you could do it like this:
class DataClass
{
…
static public Expression<Func<DataClass,bool>> MatchSelectedFields( string text, bool ignoreCase)
{
return #this => (
String.Equals( text, #this.PropertyOne, (ignoreCase? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal))
|| String.Equals( text, #this.Child.PropertyThree, (ignoreCase? StringComparison.OrdinalIgnoreCase: StringComparison.Ordinal))
);
}
}
Then the query is just
Expression<Func<DataClass,bool>> match = DataClass.MatchSelectedFields( "lorem", ignoreCase);
IEnumerable<DataClass> results = dataList.Where( d => match(d));
I wouldn't usually post a second answer but I thought it would be useful to see how to avoid dynamic modification of Expressions.
Caveat: I didn't actually try to compile it.
I want to reflect model classes of a MVC project by its name which I have in a string variable refName. Currently I am using switch case to use those class as template i.e <T>. How can we do this part better, so that if a new class comes in then I don't want to insert a new case in this switch statement.
Basically, what I want to do is to collect data from cosmos db into a specific template class format according to a field gRefType. Here's what I have done:
IEnumerable<Object> itemsRefDetail;
switch (refName)
{
case "AMCaseStatus":
itemsRefDetail = await DocumentDBRepository<AMCaseStatus>.GetItemRefDetailAsync(p => p.GrefType == refName && p.Tag == tag, collectionId);
break;
case "AMcaseSubStatus":
itemsRefDetail = await DocumentDBRepository<AMcaseSubStatus>.GetItemRefDetailAsync(p => p.GrefType == refName && p.Tag == tag, collectionId);
break;
case "AMRole":
itemsRefDetail = await DocumentDBRepository<AMRole>.GetItemRefDetailAsync(p => p.GrefType == refName && p.Tag == tag, collectionId);
break;
}
As you can see in the above code, Template class used in each case is as same as case value. All the classes have both the properties(GrefType and Tag) in common.
Here is the DocumentDbRepository class:
public static class DocumentDBRepository<T> where T : class
{
public static async Task<IEnumerable<T>> GetItemRefDetailAsync(Expression<Func<T, bool>> predicate, string collectionId)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, collectionId))
.Where(predicate)
.AsDocumentQuery();
FeedResponse<T> privilegeQueryResponse = await query.ExecuteNextAsync<T>();
return privilegeQueryResponse;
}
}
I made an example of how to reflect generic types, The only thing you should do is, wrap up the GrefType and Tag into a baseclass/interface. This might give you a startup:
The BaseType is the basetype of your data, which contains the fields you can use in your lambda function.
Data is your data service (client.CreateDocumentQuery), in this case some test data.
DocumentDBRepository contains the static SelectData method, which executes the lambda function passed as parameter.
MyLookupThing is derived from the BaseType, used in reflection.
TestReflection does the reflection and executes it.
Here's the code:
// The basetype of an item (which contains de RefName etc)
public class BaseType
{
public string RefName { get; set; }
public string Tag { get; set; }
}
// static T service with some testdata (mock)
public static class Data<T> where T : BaseType
{
public static List<T> MyData { get; } = new List<T>();
static Data()
{
// create an item
var item = Activator.CreateInstance<T>();
item.RefName = "Test";
item.Tag = "Bla";
MyData.Add(item);
var item2 = Activator.CreateInstance<T>();
item2.RefName = "SomethingElse";
item2.Tag = "TagThing";
MyData.Add(item2);
var item3 = Activator.CreateInstance<T>();
item3.RefName = "Test2";
item3.Tag = "Bla2";
MyData.Add(item3);
}
}
// the generic class which uses the basetype as generic
public static class DocumentDBRepository<T> where T : BaseType
{
public static IEnumerable<T> SelectData(Func<T, bool> predicate)
{
// some static test data
return Data<T>.MyData.Where(predicate);
}
}
// your derived class from BaseType
public class MyLookupThing : BaseType
{
}
class TestReflection
{
public TestReflection()
{
// you can create more classes derived from BaseType
var typeStr = "TestRef.MyLookupThing";
// resolve the type:
var lookupType = (from ass in AppDomain.CurrentDomain.GetAssemblies()
from t in ass.GetTypes()
where t.FullName == typeStr
select t).First();
// get the type of the generic class
var myType = typeof(DocumentDBRepository<>);
// create a generic type
var myGenericType = myType.MakeGenericType(lookupType);
var method = myGenericType.GetMethod("SelectData", BindingFlags.Static | BindingFlags.Public);
// Create the function (with the BaseType)
var func = new Func<BaseType, bool>(item => item.RefName.StartsWith("Test"));
// invoke the method of the generic class
IEnumerable<BaseType> result = (IEnumerable<BaseType>)method.Invoke(null, new object[] { func });
// show the results
foreach (var item in result)
Console.WriteLine(item.RefName);
Console.ReadKey();
}
}
Which will give:
Test
Test2
as result
Here is how you can create this expression that limits the query to the type:
internal static Expression<Func<TEntity, bool>> CreateTypeSpesificExpression<TEntity>(string refName) where TEntity : class
{
var parameter = Expression.Parameter(typeof(IAmObject));
var member = Expression.Property(parameter, nameof(IAmObject.GrefType));
var contant = Expression.Constant(refName);
var body = Expression.Equal(member, contant);
var extra = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
return extra;
}
This is all assuming that your AM classes have the same interface.
Once you do that you can simple add this in your where clause.
Your query should look like this:
IEnumerable<Object> itemsRefDetail = await DocumentDBRepository<AMCaseStatus>.GetItemRefDetailAsync(p => CreateTypeSpesificExpression(refName) && p.Tag == tag, collectionId);
Also something worth pointing out is that Cosmonaut already has native support for type based collection sharing.
My repository returns entities derived from a common base class
class BaseClass
{
public int Id { get; set; }
}
class MyClass : BaseClass
{
public string Name { get; set; }
}
class MyOtherClass : BaseClass
{
...
}
in a function like this:
IQueryable<T> GetEntities<T>() where T : BaseClass
I added a method to register additional filters for specific entities as lambdas using Expression<Func<T,bool>> like this:
RegisterFilter<MyClass>(t => t.Name == "Test" );
that will be applied whenever GetEntities is called with MyClass in the type argument.
Question
How can I create an expression dynamically at runtime that wraps a type cast around the filter?
in my specific case GetEntities is called on an IQueryable<MyClass> using the BaseClass as type argument and event tough I know that the filter for MyClass needs to applied I did not find a way to do so:
IQueryable<BaseClass> src =
(new List<MyClass>
{
new MyClass { Id = 1, Name = "asdf" },
new MyClass { Id = 2, Name = "Test" }
})
.AsQueryable();
Expression<Func<MyClass, bool>> filter = o => o.Name == "Test";
// does not work (of course)
src.Where(filter);
Failed attempts
Obviously I could cast the collection back before calling Where but my attempts to do this at runtime did not work:
// HACK: don't look
var genericCast = typeof(Queryable).GetMethod("Cast").MakeGenericMethod(entityType);
var genericWhere = typeof(Queryable).GetMethods().Single(mi => mi.Name == "Where" && mi.GetParameters()[1].ParameterType.GenericTypeArguments[0].Name == "Func`2");
q = (IQueryable<T>)genericCast.Invoke(q, new object[] { genericWhere.Invoke(q, new object[] { filterExp }) });
System.InvalidOperationException: 'Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.'
since this is also very ugly I tried to wrap my filter in cast like this:
LambdaExpression filterExp = (LambdaExpression)filter;
var call = filterExp.Compile();
Expression<Func<T, bool>> wrap = o => (bool)call.DynamicInvoke(Convert.ChangeType(o, entityType));
This works but prevents the filter to be generated into a store expression so it will be done in memory which is also undesirable.
I feel like the solution to this should be trivial but I can't seem to get it right so any help would be appreciated very much.
You can:
// Using SimpleExpressionReplacer from https://stackoverflow.com/a/29467969/613130
public static Expression<Func<BaseClass, bool>> Filter<TDerived>(Expression<Func<TDerived, bool>> test)
where TDerived : BaseClass
{
// x =>
var par = Expression.Parameter(typeof(BaseClass), "x");
// x is TDerived
var istest = Expression.TypeIs(par, typeof(TDerived));
// (TDerived)x
var convert = Expression.Convert(par, typeof(TDerived));
// If test is p => p.something == xxx, replace all the p with ((TDerived)x)
var test2 = new SimpleExpressionReplacer(test.Parameters[0], convert).Visit(test.Body);
// x is TDerived && test (modified to ((TDerived)x) instead of p)
var and = Expression.AndAlso(istest, test2);
// x => x is TDerived && test (modified to ((TDerived)x) instead of p)
var test3 = Expression.Lambda<Func<BaseClass, bool>>(and, par);
return test3;
}
Note that I'm using the SimpleExpressionReplacer I wrote in another answer.
Given a test like:
var exp = Filter<MyClass>(p => p.Name == "Test");
the resulting expression is:
x => x is MyClass && ((MyClass)x).Name == "Test"
I have an third party library that has the following API:
Update<TReport>(object updateOnly, Expression<Func<TReport,bool>> where)
What I want to do is call this method but using anonymous objects such as :
Update(new {Name = "test"}, new {Id = id})
Is it possible to take the second anonymous object and convert it to something like :
x => x.Id == id.
So what I want is to convert the new {Id = id} to a function that takes TReport and returns bool?
Even if I agree with Daniel A. White on the fact that it's complicating things, I tried a bit.
But it's not safe, cause you're losing strong typing. (You can put whatever you want in an anonymous object : it's not linked to the object's "real" properties... So no refactoring, no check...)
It's not really tested, so not sure if that's what you want. You can have (if it works) different objects in the "predicate object" :
new {Name="test"}, new{Id=1, Name="test2"})
So, you could have something like that :
public static class MyHelpers
{
public static Expression<Func<TReport, bool>> CreatePredicate<TReport>(this object predicateObject)
{
var parameterExpression = Expression.Parameter(typeof(TReport), "item");
Expression memberExpression = parameterExpression;
var objectDictionary = MakeDictionary(predicateObject);
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
var equalityExpressions = GetBinaryExpressions(objectDictionary, memberExpression).ToList();
var body = equalityExpressions.First();
body = equalityExpressions.Skip(1).Aggregate(body, Expression.And);
return Expression.Lambda<Func<TReport, bool>>(body, new[] { parameterExpression });
}
private static IDictionary<string, object> MakeDictionary(object withProperties)
{
var properties = TypeDescriptor.GetProperties(withProperties);
return properties.Cast<PropertyDescriptor>().ToDictionary(property => property.Name, property => property.GetValue(withProperties));
}
private static IEnumerable<BinaryExpression> GetBinaryExpressions(IDictionary<string, object> dic, Expression expression)
{
return dic.Select(m => Expression.Equal(Expression.Property(expression, m.Key), Expression.Constant(m.Value)));
}
}
usage, for example
public void Update<TReport>(object updateOnly, object predicateObject) {
var predicate = predicateObject.CreatePredicate<TReport>();
yourGenericApi.Update(updateOnly, predicate);
}
EDIT :
As you're losing strong typing security, you should add something like
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
after
var objectDictionary = MakeDictionary(predicateObject);
If you have a specific value that you want the function to return, I think you can simply do this:
bool desiredResult = true;
Update(new { Name = "test" }, x => desiredResult);
I found an example in the VS2008 Examples for Dynamic LINQ that allows you to use a SQL-like string (e.g. OrderBy("Name, Age DESC")) for ordering. Unfortunately, the method included only works on IQueryable<T>. Is there any way to get this functionality on IEnumerable<T>?
Just stumbled into this oldie...
To do this without the dynamic LINQ library, you just need the code as below. This covers most common scenarios including nested properties.
To get it working with IEnumerable<T> you could add some wrapper methods that go via AsQueryable - but the code below is the core Expression logic needed.
public static IOrderedQueryable<T> OrderBy<T>(
this IQueryable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(
this IQueryable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(
this IOrderedQueryable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(
this IOrderedQueryable<T> source,
string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(
IQueryable<T> source,
string property,
string methodName)
{
string[] props = property.Split('.');
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach(string prop in props) {
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
object result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] {source, lambda});
return (IOrderedQueryable<T>)result;
}
Edit: it gets more fun if you want to mix that with dynamic - although note that dynamic only applies to LINQ-to-Objects (expression-trees for ORMs etc can't really represent dynamic queries - MemberExpression doesn't support it). But here's a way to do it with LINQ-to-Objects. Note that the choice of Hashtable is due to favorable locking semantics:
using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
static class Program
{
private static class AccessorCache
{
private static readonly Hashtable accessors = new Hashtable();
private static readonly Hashtable callSites = new Hashtable();
private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(
string name)
{
var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
if(callSite == null)
{
callSites[name] = callSite = CallSite<Func<CallSite, object, object>>
.Create(Binder.GetMember(
CSharpBinderFlags.None,
name,
typeof(AccessorCache),
new CSharpArgumentInfo[] {
CSharpArgumentInfo.Create(
CSharpArgumentInfoFlags.None,
null)
}));
}
return callSite;
}
internal static Func<dynamic,object> GetAccessor(string name)
{
Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
if (accessor == null)
{
lock (accessors )
{
accessor = (Func<dynamic, object>)accessors[name];
if (accessor == null)
{
if(name.IndexOf('.') >= 0) {
string[] props = name.Split('.');
CallSite<Func<CallSite, object, object>>[] arr
= Array.ConvertAll(props, GetCallSiteLocked);
accessor = target =>
{
object val = (object)target;
for (int i = 0; i < arr.Length; i++)
{
var cs = arr[i];
val = cs.Target(cs, val);
}
return val;
};
} else {
var callSite = GetCallSiteLocked(name);
accessor = target =>
{
return callSite.Target(callSite, (object)target);
};
}
accessors[name] = accessor;
}
}
}
return accessor;
}
}
public static IOrderedEnumerable<dynamic> OrderBy(
this IEnumerable<dynamic> source,
string property)
{
return Enumerable.OrderBy<dynamic, object>(
source,
AccessorCache.GetAccessor(property),
Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> OrderByDescending(
this IEnumerable<dynamic> source,
string property)
{
return Enumerable.OrderByDescending<dynamic, object>(
source,
AccessorCache.GetAccessor(property),
Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> ThenBy(
this IOrderedEnumerable<dynamic> source,
string property)
{
return Enumerable.ThenBy<dynamic, object>(
source,
AccessorCache.GetAccessor(property),
Comparer<object>.Default);
}
public static IOrderedEnumerable<dynamic> ThenByDescending(
this IOrderedEnumerable<dynamic> source,
string property)
{
return Enumerable.ThenByDescending<dynamic, object>(
source,
AccessorCache.GetAccessor(property),
Comparer<object>.Default);
}
static void Main()
{
dynamic a = new ExpandoObject(),
b = new ExpandoObject(),
c = new ExpandoObject();
a.X = "abc";
b.X = "ghi";
c.X = "def";
dynamic[] data = new[] {
new { Y = a },
new { Y = b },
new { Y = c }
};
var ordered = data.OrderByDescending("Y.X").ToArray();
foreach (var obj in ordered)
{
Console.WriteLine(obj.Y.X);
}
}
}
Too easy without any complication:
Add using System.Linq.Dynamic; at the top.
Use vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
Edit: to save some time, the System.Linq.Dynamic.Core (System.Linq.Dynamic is deprecated) assembly is not part of the framework, but can be installed from nuget: System.Linq.Dynamic.Core
Just stumbled across this question.
Using Marc's ApplyOrder implementation from above, I slapped together an Extension method that handles SQL-like strings like:
list.OrderBy("MyProperty DESC, MyOtherProperty ASC");
Details can be found here: http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html
I guess it would work to use reflection to get whatever property you want to sort on:
IEnumerable<T> myEnumerables
var query=from enumerable in myenumerables
where some criteria
orderby GetPropertyValue(enumerable,"SomeProperty")
select enumerable
private static object GetPropertyValue(object obj, string property)
{
System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
return propertyInfo.GetValue(obj, null);
}
Note that using reflection is considerably slower than accessing the property directly, so the performance would have to be investigated.
Just building on what others have said. I found that the following works quite well.
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> input, string queryString)
{
if (string.IsNullOrEmpty(queryString))
return input;
int i = 0;
foreach (string propname in queryString.Split(','))
{
var subContent = propname.Split('|');
if (Convert.ToInt32(subContent[1].Trim()) == 0)
{
if (i == 0)
input = input.OrderBy(x => GetPropertyValue(x, subContent[0].Trim()));
else
input = ((IOrderedEnumerable<T>)input).ThenBy(x => GetPropertyValue(x, subContent[0].Trim()));
}
else
{
if (i == 0)
input = input.OrderByDescending(x => GetPropertyValue(x, subContent[0].Trim()));
else
input = ((IOrderedEnumerable<T>)input).ThenByDescending(x => GetPropertyValue(x, subContent[0].Trim()));
}
i++;
}
return input;
}
I was trying to do this but having problems with Kjetil Watnedal's solution because I don't use the inline linq syntax - I prefer method-style syntax. My specific problem was in trying to do dynamic sorting using a custom IComparer.
My solution ended up like this:
Given an IQueryable query like so:
List<DATA__Security__Team> teams = TeamManager.GetTeams();
var query = teams.Where(team => team.ID < 10).AsQueryable();
And given a run-time sort field argument:
string SortField; // Set at run-time to "Name"
The dynamic OrderBy looks like so:
query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField));
And that's using a little helper method called GetReflectedPropertyValue():
public static string GetReflectedPropertyValue(this object subject, string field)
{
object reflectedValue = subject.GetType().GetProperty(field).GetValue(subject, null);
return reflectedValue != null ? reflectedValue.ToString() : "";
}
One last thing - I mentioned that I wanted the OrderBy to use custom IComparer - because I wanted to do Natural sorting.
To do that, I just alter the OrderBy to:
query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField), new NaturalSortComparer<string>());
See this post for the code for NaturalSortComparer().
I've stumble this question looking for Linq multiple orderby clauses
and maybe this was what the author was looking for
Here's how to do that:
var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);
Use dynamic linq
just add using System.Linq.Dynamic;
And use it like this to order all your columns:
string sortTypeStr = "ASC"; // or DESC
string SortColumnName = "Age"; // Your column name
query = query.OrderBy($"{SortColumnName} {sortTypeStr}");
After a lot of searching this worked for me:
public static IEnumerable<TEntity> OrderBy<TEntity>(this IEnumerable<TEntity> source,
string orderByProperty, bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command,
new[] { type, property.PropertyType },
source.AsQueryable().Expression,
Expression.Quote(orderByExpression));
return source.AsQueryable().Provider.CreateQuery<TEntity>(resultExpression);
}
First Install Dynamic
Tools --> NuGet Package Manager --> Package Manager Console
install-package System.Linq.Dynamic
Add Namespace using System.Linq.Dynamic;
Now you can use OrderBy("Name, Age DESC")
You could add it:
public static IEnumerable<T> OrderBy( this IEnumerable<T> input, string queryString) {
//parse the string into property names
//Use reflection to get and sort by properties
//something like
foreach( string propname in queryString.Split(','))
input.OrderBy( x => GetPropertyValue( x, propname ) );
// I used Kjetil Watnedal's reflection example
}
The GetPropertyValue function is from Kjetil Watnedal's answer
The issue would be why? Any such sort would throw exceptions at run-time, rather than compile time (like D2VIANT's answer).
If you're dealing with Linq to Sql and the orderby is an expression tree it will be converted into SQL for execution anyway.
Here's something else I found interesting.
If your source is a DataTable, you can use dynamic sorting without using Dynamic Linq
DataTable orders = dataSet.Tables["SalesOrderHeader"];
EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable()
orderby order.Field<DateTime>("OrderDate")
select order;
DataView view = query.AsDataView();
bindingSource1.DataSource = view;
reference: http://msdn.microsoft.com/en-us/library/bb669083.aspx (Using DataSetExtensions)
Here is one more way to do it by converting it to a DataView:
DataTable contacts = dataSet.Tables["Contact"];
DataView view = contacts.AsDataView();
view.Sort = "LastName desc, FirstName asc";
bindingSource1.DataSource = view;
dataGridView1.AutoResizeColumns();
You can convert the IEnumerable to IQueryable.
items = items.AsQueryable().OrderBy("Name ASC");
An alternate solution uses the following class/interface. It's not truly dynamic, but it works.
public interface IID
{
int ID
{
get; set;
}
}
public static class Utils
{
public static int GetID<T>(ObjectQuery<T> items) where T:EntityObject, IID
{
if (items.Count() == 0) return 1;
return items.OrderByDescending(u => u.ID).FirstOrDefault().ID + 1;
}
}
Thanks to Maarten (Query a collection using PropertyInfo object in LINQ) I got this solution:
myList.OrderByDescending(x => myPropertyInfo.GetValue(x, null)).ToList();
In my case I was working on a "ColumnHeaderMouseClick" (WindowsForm) so just found the specific Column pressed and its correspondent PropertyInfo:
foreach (PropertyInfo column in (new Process()).GetType().GetProperties())
{
if (column.Name == dgvProcessList.Columns[e.ColumnIndex].Name)
{}
}
OR
PropertyInfo column = (new Process()).GetType().GetProperties().Where(x => x.Name == dgvProcessList.Columns[e.ColumnIndex].Name).First();
(be sure to have your column Names matching the object Properties)
Cheers
You can use this:
public List<Book> Books(string orderField, bool desc, int skip, int take)
{
var propertyInfo = typeof(Book).GetProperty(orderField);
return _context.Books
.Where(...)
.OrderBy(p => !desc ? propertyInfo.GetValue(p, null) : 0)
.ThenByDescending(p => desc ? propertyInfo.GetValue(p, null) : 0)
.Skip(skip)
.Take(take)
.ToList();
}
This answer is a response to the comments that need an example for the solution provided by #John Sheehan - Runscope
Please provide an example for the rest of us.
in DAL (Data Access Layer),
The IEnumerable version:
public IEnumerable<Order> GetOrders()
{
// i use Dapper to return IEnumerable<T> using Query<T>
//.. do stuff
return orders // IEnumerable<Order>
}
The IQueryable version
public IQueryable<Order> GetOrdersAsQuerable()
{
IEnumerable<Order> qry= GetOrders();
// use the built-in extension method AsQueryable in System.Linq namespace
return qry.AsQueryable();
}
Now you can use the IQueryable version to bind, for example GridView in Asp.net and benefit for sorting (you can't sort using IEnumerable version)
I used Dapper as ORM and build IQueryable version and utilized sorting in GridView in asp.net so easy.
You can define a dictionary from string to Func<> like this :
Dictionary<string, Func<Item, object>> SortParameters = new Dictionary<string, Func<Item, object>>()
{
{"Rank", x => x.Rank}
};
And use it like this :
yourList.OrderBy(SortParameters["Rank"]);
In this case you can dynamically sort by string.
you can do it like this for multiple order by
IOrderedEnumerable<JToken> sort;
if (query.OrderBys[0].IsDESC)
{
sort = jarry.OrderByDescending(r => (string)r[query.OrderBys[0].Key]);
}
else
{
sort = jarry.OrderBy(r =>
(string) r[query.OrderBys[0].Key]);
}
foreach (var item in query.OrderBys.Skip(1))
{
if (item.IsDESC)
{
sort = sort.ThenByDescending(r => (string)r[item.Key]);
}
else
{
sort = sort.ThenBy(r => (string)r[item.Key]);
}
}
Convert List to IEnumerable or Iquerable, add using System.LINQ.Dynamic namespace, then u can mention the property names in comma seperated string to OrderBy Method which comes by default from System.LINQ.Dynamic.
I am able to do this with the code below. No need write long and complex code.
protected void sort_array(string field_name, string asc_desc)
{
objArrayList= Sort(objArrayList, field_name, asc_desc);
}
protected List<ArrayType> Sort(List<ArrayType> input, string property, string asc_desc)
{
if (asc_desc == "ASC")
{
return input.OrderBy(p => p.GetType()
.GetProperty(property)
.GetValue(p, null)).ToList();
}
else
{
return input.OrderByDescending(p => p.GetType()
.GetProperty(property)
.GetValue(p, null)).ToList();
}
}
If you are using Specification (such as Ardalis Specification)
using Microsoft.EntityFrameworkCore;
namespace TestExtensions;
public static class IQueryableExtensions
{
public static void ApplyOrder<T>(ISpecificationBuilder<T> query, string propertyName, bool ascendingOrder)
{
if (ascendingOrder)
query.OrderBy(T => EF.Property<object>(T!, propertyName));
else
query.OrderByDescending(T => EF.Property<object>(T!, propertyName));
}
}
With Net6 and EF
.AsQueryable().OrderBy((ColumnOrder.Column, ColumnOrder.Dir));
var result1 = lst.OrderBy(a=>a.Name);// for ascending order.
var result1 = lst.OrderByDescending(a=>a.Name);// for desc order.