I am trying to order an IEnumerable<MyClass> based on the index of the selected property:
class MyClass
{
public string Name;
public string Value;
}
I have a helper method to get an Expression<> of the criteria.
It is compiled to create the Func<> to help with the ordering.
I'm having trouble trying to make the Func<> return a Generic TKey to be ordered.
public Expression<Func<T, TKey>> Helper<T>(int propIdx)
{
var param = Expression.Parameter(typeof(T), "record");
var props = typeof(T).GetProperties();
if(propIdx > props.Count())
return Expression.Lambda<Func<T, TKey>>)(Expression.Constant(true), param);
Expression propExp = Expression.Property(param, props[propIdx].Name);
return lambda = Expression.Lambda<Func<T, TKey>>(propExp, param);
}
This is used to help with dynamic order criteria.
OrderByIdx(IEnumerable<MyClass> input, int propIdx)
{
Expression<Func<T, TKey>> exp = Helper(propIdx);
var funct = exp.Compile();
return input.OrderBy(funct);
}
I am getting an error The type or namespace TKey could not be found
How can I use a Generic<TKey> to help with the ordering ?
First of all: Type.GetProperties doesn't have a defined order.
var props = typeof(T).GetProperties();
Which property will be in props[0]? And are all properties readable?
So if you want to get a defined order, my advice would be to do some sorting. For instance get the public readable properties ordered by name.
Furthermore, isn't it a bit weird if users of your code (= software, not operators) want to use your generic code based on an index?
They have a class MyClass with a property Date
They have a sequence of objects of this class.
They want to order by property Date
Instead of ordering by property Date, or by property named "Date", They have to determine the index of property Date: Date seems to be the fourth property
Then they have to call your method using this index: "OrderBy index 4"
Wouldn't it be easier if they could just say: "order by property Date", or "order by the property that is named 'Date'?
IEnumerable<MyClass> source = ...
var orderedSource = source.OrderBy(t => t.Date);
Sometimes your users don't have access to the property, they only know the name of the property. In that case you could create an extension method where you provide the name of a property, and that returns the ordered source.
If you are not familiar with extension methods. See extension methods demystified
Usage would be something like:
var orderedSource = source.OrderBy(nameof(MyClass.Date));
or for instance: "order by the selected column in my DataGridView".
string propertyName = this.DataGridView.Columns.Cast<DataGridViewColumn>()
.Where(column => column.IsSelected)
.Select(column => column.DataPropertyName)
.FirstOrDefault();
var orderedSource = source.OrderBy(propertyName);
Such a procedure would be easy to make, easy to use and reuse, easy to test, easy to maintain. And above all: they are one-liners:
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
string propertyName)
{
// TODO: handle invalid input
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
// TODO: handle invalid propertyname
return source.OrderBy(propertyInfo);
}
public static IOrderedEnumerable<T> OrderBy<T>(
this IEnumerable<T> source,
PropertyInfo propertyInfo)
{
// TODO: handle invalid parameters
return source.OrderBy(t => propertyInfo.GetValue(t) as propertyInfo.PropertyType);
}
If desired you could create overloads for ThenBy(this IEnumerable<T>, ...).
But I want to order by Index, not by Name!
If you really want to sort by index, it is easier to create extension methods with Lambda expressions, then to fiddle with Expressions.
Consider the following extension methods:
public static IOrderedEnumerable<T> OrderBy<T>(
IEnumerable<T> source,
int propertyIndex)
{
return source.OrderBy(propertyIndex, GetDefaultPropertyOrder(typeof(T));
}
public static IOrderedEnumerable<T> OrderBy<T>(
IEnumerable<T> source,
int propertyIndex,
IReadOnlyList<PropertyInfo> properties)
{
// TODO: handle null and out-of-range parameters
PropertyInfo sortProperty = properties[propertyIndex];
return source.OrderBy(sortProperty);
}
public static IList<PropertyInfo> GetDefaultPropertyOrder(Type t)
{
return t.GetProperties()
.Where(property => property.CanRead)
.OrderBy(property => property.Name)
.ToList();
}
Usage:
BindingList<MyClass> myObjects = ...
// display myObjects in a dataGridView
this.DataGridView.DataSource = myObjects;
// event if operator clicks on column header:
public void OnColumnHeaderClicked(object sender, ...)
{
DataGridViewColumn clickedColumn = GetClickedColumn();
// sort by column index, as you prefer:
var sortedObjects = myObjects.SortBy(clickedColumn.DisplayIndex);
ProcessSortedObjects(sortedObjects);
// but of course, you skip some intermediate methods if you sort by property name
var sortedObject = myObject.SortBy(clickedColumn.DataPropertyName);
ProcessSortedObjects(sortedObjects);
}
To be able to order on a property, it must be of a type implementing IComparable. You can use this fact to replace TKey by IComparable in your code:
class MyClass
{
public string Name { get; set; }
public string Value { get; set; }
}
public Expression<Func<T, IComparable>> Helper<T>(int propIdx)
{
var param = Expression.Parameter(typeof(T), "record");
var props = typeof(T).GetProperties();
Expression propExp = Expression.Property(param, props[propIdx].Name);
return Expression.Lambda<Func<T, IComparable>>(propExp, param);
}
IEnumerable<MyClass> OrderByIdx(IEnumerable<MyClass> input, int propIdx)
{
Expression<Func<MyClass, IComparable>> exp = Helper<MyClass>(propIdx);
var funct = exp.Compile();
return input.OrderBy(funct);
}
Related
I have list of dynamic objects where I want to query by custom property. In other words, it would look like this if I wasn't going for reflection:
IEnumerable<User> FilterUsers(IEnumerable<User> users, string selectedValue)
{
users.Where(user => user.Name == selectedValue);
}
So far I've come up with the following implementation that works if users is typed:
IEnumerable<User> FilterUsers(IEnumerable<User> users, string selectedField, string selectedValue)
{
LabelTarget returnTarget = Expression.Label(typeof(bool));
ParameterExpression userParameter = Expression.Parameter(typeof(User));
MemberExpression userSelectedField = Expression.Property(userParameter, selectedField);
Expression test = Expression.Equal(userSelectedField, Expression.Constant(selectedValue));
Expression iftrue = Expression.Return(returnTarget, Expression.Constant(true));
Expression iffalse = Expression.Return(returnTarget, Expression.Constant(false));
var ex = Expression.Block(
Expression.IfThenElse(test, iftrue, iffalse),
Expression.Label(returnTarget, Expression.Constant(false)));
var whereClause = Expression.Lambda<Func<User, bool>>(
ex,
new ParameterExpression[] { userParameter }
).Compile();
return users.Where(user => whereClause(user));
}
What I am really trying to do is to make users dynamic object:
IEnumerable<dynamic> FilterUsers(IEnumerable<dynamic> users, string selectedField, string selectedValue) {
// ...
ParameterExpression userParameter = Expression.Parameter(typeof(object)); // ???
MemberExpression userSelectedField = Expression.Property(userParameter, selectedField); // throws
// ...
}
This throws the following exception: Instance property 'Name' is not defined for type 'System.Object' (Parameter 'propertyName'). What am I missing?
Alternatively, how can I use Dictionary<string, object>?
Using dynamic here doesn't get you much: you'd be better off using generics if you can:
IEnumerable<T> FilterUsers<T>(IEnumerable<T> users, string selectedField, string selectedValue)
{
var userParameter = Expression.Parameter(typeof(T));
var userSelectedField = Expression.Property(userParameter, selectedField);
// etc...
}
If you do need to use dynamic, then you'll need to get the runtime type of each user, using .GetType(). However bear in mind that there's nothing stopping someone from passing in an IEnumerable containing lots of different types of object, and they don't all have to have a property called selectedField!
Or, they might pass in lots of different types of object, each of one has a property called selectedField, but they're distinct properties (e.g. class A { public string Foo { get; set; } } and class B { public string Foo { get; set; } } -- those two Foo properties are distinct).
So you'll have to call .GetType() on each one of them, which means you won't be able to get the performance benefits of using compiled expressions.
If you can guarantee that all elements have the same type, you can do something like:
private static IEnumerable<dynamic> FilterCollection(IEnumerable<dynamic> collection, string property, string value)
{
if (!collection.Any()) return collection;
var collectionItemType = collection.First().GetType();
var userParameter = Expression.Parameter(typeof(object));
var convertedUser = Expression.Convert(userParameter, collectionItemType);
var userSelectedField = Expression.Property(convertedUser, selectedField);
...
}
Beware however that you're enumerating users twice, which is probably a bad thing. You might do better to get the IEnumerator yourself and work with it explicitly.
As #canton7 said you should be using a generic method. I also see in your question you specified you're looking for properties, why not use regular old reflection?
public static IEnumerable<T> FilterItems<T>(IEnumerable<T> items, string property, string value)
{
var prop = typeof(T).GetProperties().First(p => p.Name == property);
return items.Where(i => prop.GetValue(i).ToString().Contains(value));
}
Of course that code should be enhanced to handle different errors....
I'm implementing the back end of a search/filter UI for an app using EF6. I have code that builds an Expression to use with Queryable.Where for a given DbSet, where the type of the DbSet is determined at runtime (the DBContext has a lot of them, and they may change). The call to Where works fine if I cheat by casting the Expression to a specific type first. Otherwise, I get this error:
'The best overloaded method match for 'System.Linq.Queryable.Where(System.Linq.IQueryable, System.Linq.Expressions.Expression>)' has some invalid arguments'
I'm struggling to find a way to filter the DbSet like this where the underlying 'table' type is provided at runtime. Here's a greatly simplified version of the code to illustrate:
void ProcessFilter(AppDbContext context, NameValueCollection filters, Type tableType)
{
// If tableType == typeof(Organisation), expression is a Expression<Func<Organisation, bool>>
var expression = GetFilterExpression(filters);
var dbset = Set(context, tableType);
dynamic dynamicSet = dbset;
// This fails
var results = Queryable.Where(dynamicSet, expression);
// see https://stackoverflow.com/questions/4285598/iqueryable-non-generic-missing-count-and-skip-it-works-with-iqueryablet
// Suppose tableType == typeof(Organisation)
// This works
var typedExpression = expression as Expression<Func<Organisation, bool>>;
var typedResults = Queryable.Where(dynamicSet, typedExpression);
}
public static IQueryable Set(DbContext context, Type T)
{
// Similar to code in
// https://stackoverflow.com/questions/21533506/find-a-specified-generic-dbset-in-a-dbcontext-dynamically-when-i-have-an-entity
var method = typeof(DbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == "Set" && x.IsGenericMethod).First();
// Build a method with the specific type argument
method = method.MakeGenericMethod(T);
return method.Invoke(context, null) as IQueryable;
}
Answering you concrete question. Given
IQueryable source
LambdaExpression predicate
how to call the static generic method
Queryable.Where<T>(IQueryable<T> source, Expression<Func<T, bool>> predicate)
It can be done using (A) reflection, (B) DLR dynamic dispatch and (C) Expression.Call.
What you are trying to do is option (B). However
var result = Queryable.Where((dynamic)source, predicate);
does dynamic search for method having second argument of type LambdaExpression, which of course fails.
In order to be able to dynamically match the target method, you need to make the second argument dynamic as well:
var result = Queryable.Where((dynamic)source, (dynamic)predicate);
The equivalent option (C) implementation of the above is:
var result = source.Provider.CreateQuery(Expression.Call(
typeof(Queryable), nameof(Queryable.Where), new[] { source.ElementType },
source.Expression, predicate));
congrats on your first question.
Let's begin by looking at an approach for filtering a collection of data based on some custom filters. I will assume that the NameValueCollection Type you prefer to pass in your filters, holds PropertyNames as Keys and PropertyValues as Value.
Before we go forth filtering an entire collection, let's first figure out how to determine whether one object has properties that match our filters. And since we do not know the Type of our object till runtime, we will need to use Generics in C# to accomplish this.
Step 1
- Get All Class Properties
We will need to get all properties of our generic class, e.g <TClass>. Doing this using Reflection is deemed as slow and Matt Warren explains Why Reflection is slow in .NET and how to work around it. We shall therefore implement caching of class component model to get its PropertyDescriptorCollection which exists in the namespace System.ComponentModel.PropertyDescriptorCollection.
Components Cache
private static IDictionary<string, PropertyDescriptorCollection> _componentsCache
= new Dictionary<string, PropertyDescriptorCollection>();
The key of our Dictionary represents the name of the generic class and the value holds the PropertyDescriptorCollection of that given class.
internal static bool InnerFilter<T>(T obj, NameValueCollection filters)
where T : class
{
Type type = typeof(T);
PropertyDescriptorCollection typeDescriptor = null;
if (_componentsCache.ContainsKey(type.Name))
typeDescriptor = _componentsCache[type.Name];
else
{
typeDescriptor = TypeDescriptor.GetProperties(type);
_componentsCache.Add(type.Name, typeDescriptor);
}
}
Step 2
- Loop through filters
After we have gotten the PropertyDescriptorCollection for the generic class T in the variable typeDescriptor as shown above, now let's loop through our filters and see if any of its property names match any of our filter keys. If T has a property name that matches any of our filter keys, now we inspect if the actual value of the property matches our filter value. To improve the quality of our search/filter function, we are going to use Regular Expressions in C# to determine whether a comparison is a hit or a miss.
for (int i = 0; i < filters.Count; i++)
{
string filterName = filters.GetKey(i);
string filterValue = filters[i];
PropertyDescriptor propDescriptor = typeDescriptor[filterName];
if (propDescriptor == null)
continue;
else
{
string propValue = propDescriptor.GetValue(obj).ToString();
bool isMatch = Regex.IsMatch(propValue, $"({filterValue})");
if (isMatch)
return true;
else
continue;
}
}
Step 3
- Implement Extension Methods.
To make the code that we've written easy to use and re-use, we are going to implement Extension Methods in C# so that we can better re-use our functions anywhere within our project.
- Generic Collection Filter Function that Uses the above Function.
Since an IQueryable<T> can be converted to an IEnumerable<T> by the .Where() function in System.Linq, we are going to utilize that in our function call as shown below.
public static IEnumerable<T> Filter<T>(this IEnumerable<T> collection, NameValueCollection filters)
where T : class
{
if (filters.Count < 1)
return collection;
return collection.Where(x => x.InnerFilter(filters));
}
Step 4
Put everything together.
Now that we have everything we need, let's look at how the final/full code looks as one block of code in a single static class.
public static class Question54484908
{
private static IDictionary<string, PropertyDescriptorCollection> _componentsCache = new Dictionary<string, PropertyDescriptorCollection> ();
public static IEnumerable<T> Filter<T> (this IEnumerable<T> collection, NameValueCollection filters)
where T : class
{
if (filters.Count < 1)
return collection;
return collection.Where (x => x.InnerFilter (filters));
}
internal static bool InnerFilter<T> (this T obj, NameValueCollection filters)
where T : class
{
Type type = typeof (T);
PropertyDescriptorCollection typeDescriptor = null;
if (_componentsCache.ContainsKey (type.Name))
typeDescriptor = _componentsCache[type.Name];
else {
typeDescriptor = TypeDescriptor.GetProperties (type);
_componentsCache.Add (type.Name, typeDescriptor);
}
for (int i = 0; i < filters.Count; i++) {
string filterName = filters.GetKey (i);
string filterValue = filters[i];
PropertyDescriptor propDescriptor = typeDescriptor[filterName];
if (propDescriptor == null)
continue;
else {
string propValue = propDescriptor.GetValue (obj).ToString ();
bool isMatch = Regex.IsMatch (propValue, $"({filterValue})");
if (isMatch)
return true;
else
continue;
}
}
return false;
}
}
FINALLY
Filtering IEnumerable<T>, List<T>, Arrays
This is how you are going to use the above code anywhere in your project.
private IEnumerable<Question> _questions;
_questions = new List<Question>()
{
new Question("Question 1","How do i work with tuples"),
new Question("Question 2","How to use Queryable.Where when type is set at runtime?")
};
var filters = new NameValueCollection
{
{ "Description", "work" }
};
var results = _questions.Filter(filters);
Filtering DbSet<T>
Every DbContext has a function .Set<T> that returns a DbSet<T> that can be used as an IQueryable<T> and thus our function can be used as well as shown below.
Example
_dbContext.Set<Question>().Filter(filters);
Hope this answers your question or rather points you in the right direction.
I'm building Linq Extension methods.
Shortly, I've built an extension method in order to create a MemberExpression looks like:
public static Expression Field<T>(this object entity, string field)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Property(parameterExpression, propertyInfo);
}
So, I'm able to do that:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").EndsWith(string.Empty)<<<<<<<< Compilation error.
);
Since MemberExpression have not EndsWith method, I'm not able to extend this MemberExpression like a String property access like:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Matter.EndsWith(string.Empty)
);
Is there some way to do that.
As you are able to figure out I'm trying to get something a bit more complex, Nevertheless, this example is for explaining the situation.
I hope it's enought clear.
Scope
My UI is using a backend.
This backend have three implementations. Each one of them provides a Linq implementation (Linq collections, NHibernate, custom-made Linq provider).
So, my UI is able to work on collections, a database or getting data from our server.
I'd like to provide util extension methods like AnyField().
So, after digging a bit I'm thinking on two approaches:
AnyField() generates an expression tree which is able to be translated by every Linq provider (first answer of this post).
Provide a default implementation of Anyfield() for Linq Collections, and then use each Linq provider extension mechanism for handle it. Or, if you are building a Linq Provider, support it on implementation.
Okay, so you're getting tripped up on the syntactic sugar that C# provides for you when building ExpressionTrees
Where expects Expression<Func<TObjectType, TReturnType>> or a compiled lambda; Func<TObjectType, TReturnType>.
Your method Field currently only returns an untyped Expression. That means your query is actually returning Expression<Func<TObjectType, Expression>>. That's not right! It should be returning a Expression<Func<TObjectType, string>>! But how do we do that? That would mean our method would have to return a string, but we want to build an expression tree.
To get it working as you're expecting, it's quite a bit more difficult than you would imagine, but that's only because we're so spoiled with the syntactic sugar.
What we actually need to do is write methods which accept lambda methods, and return lambda methods, each one re-writing the body a little bit.
So... what does that look like?
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
Notice that it's almost the exact same as what you wrote, but we wrap it with Lambda<Func<TElementType, TReturnType>>. And the signature is a bit different too.
Instead of operating on an object, we want to operate on a lambda expression. We also return a lambda expression.
So how do we use it?
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
);
Great! Now we're passing Expression<Func<Test, string>> to Where, rather than Expression<Func<Test, MemberExpression>>. Making progress.
But that won't compile, and rightly so. We're returning a string, but we're using a filtering method, which requires a bool.
So let's now write EndsWith:
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
And using it:
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
.EndsWith("A")
);
Which is now compiling! And the expression tree looks like this:
UserQuery+Test[].Where(e => e.Matter.EndsWith("A"))
That's not too pretty, having Field take a redundant lambda, though. Let's add a helper method to make it look prettier:
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
Putting it all together:
void Main()
{
var objects = new[] { new Test { Matter = "A" } }.AsQueryable();
var classes = objects.Where(
ExpressionExtensions.Query<Test>(q => q)
.Field("Matter")
.EndsWith("A")
);
classes.Expression.Dump();
}
public class Test
{
public string Matter { get; set;}
}
public static class ExpressionExtensions
{
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
}
I don't know if you mess the code to show a piece of code or if it was intended, but you have a generic extension that wants A T but you don't use it
Anyway if what you want is a method that returns you the value of a property, why don't you do a static exception that return T ?
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
this is a fiddle i've done to show you the usage, pretty simple
https://dotnetfiddle.net/PoSfli
posting the code too in case fiddle get lost:
using System;
using System.Reflection;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
YourClass c = new YourClass() {
PropA = 1,
PropB = 2,
PropC = "ciao"
};
var propBValue = c.Field<int>("PropB");
Console.WriteLine("PropB value: {0}", propBValue);
var propCValue = c.Field<string>("PropC");
Console.WriteLine("PropC value: {0}", propCValue);
}
}
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
public class YourClass {
public int PropA { get; set; }
public int PropB { get; set; }
public string PropC { get; set; }
}
nota that you can improve a lot, using a typed extension and a property expression as argument instead of a string
You can also do something really simple like if you want to use the property name:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").ToString().EndsWith(string.Empty)
Or if your are filtering by property type:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Type.ToString().EndsWith(string.Empty)
How about something like this? Actually, your generic approach is not of use right now.
public static bool Evaluate<TField>(this object entity, string fieldName, Predicate<TField> condition)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
var value = (TField)propertyInfo.GetValue(entity); //read the value and cast it to designated type, will raise invalid cast exception, if wrong
return condition.Invoke(value); //invoke the predicate to check the condition
}
Usage would be then.
.Where(item => item.Evaluate<string>("Matter", prop => prop.EndsWith(string.Empty))
You can add a new extension method which returns your desired type.
public static T Compile<T>(this Expression expression)
{
return Expression.Lambda<Func<T>>(expression).Compile()();
}
In your statement you just have to add .Compile<type>()
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Compile<string>().EndsWith(string.Empty));
This problem has been discussed to an extent in this question: Create Generic Expression from string property name but perhaps I'm missing the answer, or it is subtly different.
I have the following queryable extension method:
public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
var sourceType = typeof (TSource);
var parameter = Expression.Parameter(sourceType, "item");
var orderByProperty = Expression.Property(parameter, propertyName);
var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderByDescending(source, (dynamic) orderBy);
}
For the purposes of this problem let us assume that the queryable source instance has a property called 'Created' which is a type of DateTime. If we define a class that has the property 'Created' on it and then OrderByDescending then the above will work fine. e.g.
var queryable = new List<EntityClassWithCreatedProperty>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
If we do the same thing but with an interface such as ICreated which has the Created property on it:
public interface ICreated
{
DateTime Created { get; }
}
Then the following also works:
var queryable = new List<ICreated>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
If however, you have the following interface hierarchy:
public interface ITimestamped : ICreated
{
...
}
Then the following will fail:
var queryable = new List<ITimestamped>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
With the similar error message to that of the other question: Instance property 'Created' is not defined for type ITimestamped. I'm assuming that I need to find the property on the declaring type of interface (which I can do by crawling the source type) but then what do I do with it? Most attempts I have tried result in the incorrect original source type not being cast-able back to the IQueryable. Do I need to use a ConvertType call somewhere? Thanks.
I would use the other Expression.Property() method that takes a propertyInfo, e.g
public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
var sourceType = typeof (TSource);
var parameter = Expression.Parameter(sourceType, "item");
var propertyInfo = FindMyProperty(sourceType, propertyName);
var orderByProperty = Expression.Property(parameter, propertyInfo);
var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderByDescending(source, (dynamic) orderBy);
}
private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
return type.GetProperty(propertyName);
}
At this point your question has nothing to do with expressions, it's a simple reflection question, and you have to find the correct way to get the properties you want. There are a lot of scenarios here. For your exact one, meaning get property from parent interface, you can do something like:
private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
var result = type.GetProperty(propertyName);
if (result == null)
{
foreach(var iface in type.GetInterfaces())
{
var ifaceProp = FindMyProperty(iface, propertyName);
if (ifaceProp != null)
return ifaceProp;
}
}
return result;
}
DISCLAIMER!
This is by no means the best way to get a property from a type, but it should work in your case. You should google around to find an implementation for FindMyProperty that satisfies all your requirements :)
We have some code that given a property name uses reflection to implement a Comparer.
I would like to store a delegate/Func to get the value rather than paying the reflection price each time we need to get a value.
Given a class like this:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
I tried to write a function that would create a delegate for me
Func<T, object> CreateGetFuncFor<T>(string propertyName)
{
PropertyInfo prop = typeof(T).GetProperty(propertyName);
return (Func<T, object>)Delegate.CreateDelegate(typeof(Func<T, object>),
null,
prop.GetGetMethod());
}
The following code works fine for the getting the Name
var person = new Person { Name = "Dave", Age = 42 };
var funcitonToGetName = CreateGetFuncFor<Person>("Name");
Console.WriteLine(funcitonToGetName(person));
var functionToGetAge = CreateGetFuncFor<Person>("Age");
but for the Age proerty it throws an ArgumentException with the message "Error binding to target method"
What am I missing? Is there another way to do it?
It seems odd that you know the declaring type at compile-time but not the property type. Anyway...
You'll need an extra step to convert the property value to an object so that it matches the Func<T,object> delegate's return type. (The extra step isn't strictly necessary for reference-typed properties, but doesn't do any harm.)
Func<T, object> CreateGetFuncFor<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "obj");
var property = Expression.Property(parameter, propertyName);
var convert = Expression.Convert(property, typeof(object));
var lambda = Expression.Lambda(typeof(Func<T, object>), convert, parameter);
return (Func<T, object>)lambda.Compile();
}
Its probably because Age is essentially defined as:
public int Age {get; private set;}
and a method returning an int is not implicitly convertible to a method returning an object, whereas String is.
try:
Func<T, R> CreateGetFuncFor<T, R>(string propertyName)
{
PropertyInfo prop = typeof(T).GetProperty(propertyName);
return (Func<T, R>)Delegate.CreateDelegate(typeof(Func<T, R>),
null,
prop.GetGetMethod());
}
and then
var functionToGetAge = CreateGetFuncFor<Person, int>("Age");