I want to sort on a field only known at runtime, and that field may be constructed inside the select clause.
Requirements:
User should be able to sort on any column;
That column may come from an entity or projection class;
Programmer decides if want to return the same source entity, or a projection.
Because of (3), straightforward implementation wouldn't find a field from projection class, as I have a static type of the base/entity type. Then I began to change code to consider the resulting type instead.
I have a feeling that it is just static typing getting in the way, that is not really helping here. Unfortunately IQueryable<dynamic> doesn't work.
Sample code (details omitted for brevity):
void Main()
{
using (var db = new MyContext())
{
var original = db.Set<Sample>();
// requirement (3), static type Sample, dynamic type SampleDTO
Expression<Func<Sample, Sample>> fn = item => new SampleDTO() { Description = "Value: " + item.Name};
var projected = original.Select(fn);
// requirements (1) and (2)
var ordered = OrderBy_Default<Sample, SampleDTO>(projected, "Description");
ordered.Dump();
}
}
private IQueryable<T> OrderBy_Default<T, TProject>(IQueryable<T> source, string ordering)
{
var type = source.ElementType;
var resultType = type;
var property = resultType.GetProperty(ordering, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
var parameter = Expression.Parameter(resultType, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
"OrderBy",
new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
The previous code produces this error:
No generic method 'OrderBy' on type 'Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
I have tried projected.Cast<SampleDTO>():
Unable to cast the type 'Sample' to type 'UserQuery+SampleDTO'. LINQ to Entities only supports casting EDM primitive or enumeration types.
I have tried projected.OfType<SampleDTO>():
'UserQuery+SampleDTO' is not a valid metadata type for type filtering operations. Type filtering is only valid on entity types and complex types.
Cast() and OfType() works with L2O, but not with L2EF.
Adding SampleDTO to entity model of course is not the answer (have tried it though, for the sake of knowing how it would work).
My entities & projections:
public class Sample
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SampleDTO: Sample
{
public string Description { get; set; }
}
I made SampleDTO inherit from Sample, it case it helps. That's is not a requirement, it's a matter of convenience.
Some possible answer:
Instead of Expression<Func<Sample, Sample>> I could have Expression<Func<Sample, SampleDTO>>. That invalidates requirement (3) and would break much of my existing code.
PS: I'm considering using the Microsoft Dynamic LINQ library, but for the sake of the answer I'm looking forward to know what's really going on.
This has worked for me, if i understand your question correctly but i get the feeling i am missing something
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering)
{
var type = typeof(T);
var property = type.GetProperty(ordering);
var parameter = Expression.Parameter(type);
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
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....
The answer of Marc Gravell in this post explains how to create expressions just like that:
var lambda = CreateExpression<SomeModel, bool>("IsAlive");
It's possible avoid the explicit type bool and that the method obtains the return type from "IsAlive" property?
Something like this:
var lambda = CreateExpression<SomeModel>("IsAlive");
And lambda will be a Expression<Func<SomeModel, bool>>.
And in:
var lambda = CreateExpression<SomeModel>("StringProperty");
lambda will be a Expression<Func<SomeModel, string>>.
You can in fact do this, but the type becomes unknown. The object returned from this method will be of the proper type Expression<Func<Test, string>> but cannot be strong typed at compile time:
static LambdaExpression CreateExpression<TModel>(string propertyName)
{
var t = typeof(TModel);
var param = Expression.Parameter(typeof(TModel), "x");
//get the type for the 2nd generic arg
var propType = t.GetProperty(propertyName).PropertyType;
//make the generic type Func<TModel, TProp>
Type genericFuncType = typeof(Func<,>).MakeGenericType(new Type[] { typeof(TModel), propType });
//get the Expression.Lambda method
MethodInfo mi = typeof(Expression).GetMethods().First(a => a.Name == "Lambda" && a.GetParameters().Length == 2);
//get the Expression.Lambda<Func<TModel, TProp>> method
MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { genericFuncType });
//Call Expression.Lambda<Func<TModel, TProp>>
return (LambdaExpression)mi2.Invoke(null, new object[] { Expression.PropertyOrField(param, propertyName), new ParameterExpression[] { param }});
}
However note that the return type is now somewhat unknown and will need cast to use (or use dynamic).
So now you need even more code to cast it. Perhaps this would be useful in some sort of factory or such - not sure your use case.
class Program
{
public static void Main(string[] args)
{
var theExpression = CreateExpression<Test>("Name");
var theExpressionStrongType = theExpression as Expression<Func<Test, string>>;
//now you could use theExpressionStrongType
//or do this and go wild. :)
dynamic d = theExpression;
Console.ReadKey();
}
}
class Test
{
public string Name { get; set; }
}
Disclaimer: if you seriously want to use this in a production environment, I would clean up the code a LOT to just get refection types once, etc, etc...
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 :)
I am trying to create a query based on some JSON, I currently have the JSON parsed into a set of rules, each rule contains the name of the field, the type of comparison (=, > etc) and the value to compare.
The issue I am having is getting it from that rule, to an IQueryable object, I am guessing I need to use reflection and somehow build the expression tree, but I'm not sure on the right approach...
Assuming I have:
public class Order : BaseEntity
{
public int OrderID{ get; set; }
}
and I have the rule which is:
public class Rule
{
public string field { get; set; }
public Operations op { get; set; }
public string data { get; set; }
}
Running it I get:
field = "OrderID"
op = "eq"
data = "123"
I have the method to parse it with the signature:
public IQueryable<T> FilterObjectSet<T>(IQueryable<T> inputQuery) where T : class
As part of this method I want to do:
inputQuery = inputQuery.Where(o => propertyInfo.Name == rule1.data);
This doesn't work because it basically just generates the sql "OrderID" = "123" which is obviously wrong, I need it to take the column name from inputQuery that has the same name as propertyInfo.Name and build the query that way...
Hope that made sense? Any suggestions?
Edit: I guess what I am asking is to convert a string (Because I can build one pretty simply from the rule) to an expression, maybe using Dynamic LINQ?
Something like this:
public static IQueryable<T> FilterObjectSet<T>(IQueryable<T> inputQuery,
Rule rule) where T : class
{
var par = Expression.Parameter(typeof(T));
var prop = Expression.PropertyOrField(par, rule.field);
var propType = prop.Member.MemberType == System.Reflection.MemberTypes.Field ?
((FieldInfo)prop.Member).FieldType :
((PropertyInfo)prop.Member).PropertyType);
// I convert the data that is a string to the "correct" type here
object data2 = Convert.ChangeType(rule.data,
propType,
CultureInfo.InvariantCulture);
var eq = Expression.Equal(prop, Expression.Constant(data2));
var lambda = Expression.Lambda<Func<T, bool>>(eq, par);
return inputQuery.Where(lambda);
}
If you need some explanation, you can ask. Note that this won't work on types that have special implicit conversions (like a MyString type that has an implicit conversion from string). This because Convert.ChangeType uses only the IConvertible interface.
Null handling for data is perhaps something else that should be handled.
Be aware that I'm not sure the Expression.PropertyOrField is handled by the various IQueryable<T> engines (LINQ-to-SQL and EF). I have only tested it with the AsQueryable() engine. If they don't "accept" it, you must split it in a Expression.Property or Expression.Field depending on what rule.field is.
A nearly equivalent version that doesn't use Expression.PropertyOrField:
public static IQueryable<T> FilterObjectSet<T>(IQueryable<T> inputQuery,
Rule rule) where T : class
{
Type type = typeof(T);
var par = Expression.Parameter(type);
Type fieldPropertyType;
Expression fieldPropertyExpression;
FieldInfo fieldInfo = type.GetField(rule.field);
if (fieldInfo == null)
{
PropertyInfo propertyInfo = type.GetProperty(rule.field);
if (propertyInfo == null)
{
throw new Exception();
}
fieldPropertyType = propertyInfo.PropertyType;
fieldPropertyExpression = Expression.Property(par, propertyInfo);
}
else
{
fieldPropertyType = fieldInfo.FieldType;
fieldPropertyExpression = Expression.Field(par, fieldInfo);
}
object data2 = Convert.ChangeType(rule.data, fieldPropertyType);
var eq = Expression.Equal(fieldPropertyExpression,
Expression.Constant(data2));
var lambda = Expression.Lambda<Func<T, bool>>(eq, par);
return inputQuery.Where(lambda);
}
In the end I used a Dynamic Linq library I found on Guthrie's Blog:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Using this I was able to properly parse out and use the parameters I had built into the rules
Edit I think I can ask better (no code needed at all in this case). So the question in general is: how to use an Expression tree to build a call to a generic method (Select<TEntity,TResult> in my case) when TResult is created at runtime? Ignore all the code and text below, that was unclear version of the question, left it to not confuse those who answered.
I need an example how to build an Expression tree for a "Select" call. The problem is that I don't know the result type at compile time (it can be defined by user through some GUI in runtime). Here is some code of how I'm trying to do this (remember I can't use any generics)
class Pet {
public int Id { get; set; }
public string Name { get; set; }
}
Using this class in Main:
List<Pet> list = new List<Pet>();
Expression eList = Expression.Constant(list);
ParameterExpression pe = Expression.Parameter(typeof(Pet), "p");
MethodInfo method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
Expression selectorBody = Expression.Call(method, Expression.Constant(properties));
Expression selector = Expression.Lambda(selectorBody, pe);
Expression res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);
What I'm trying to do is create a Type at runtime using Reflection.Emit (method "CreateType" in code above, it is used in some of my projects and looks fine) and somehow pass it to call to "Select", but I get exception:
No generic method 'Select' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments.
Any suggestions?
Upd my real problem is building an Expression tree for a "Join" call for runtime types which is more complicated, so i decided to ask about "Select", because I'm having the same exception in the last line (Expression.Call(...))
Upd2 included my helper methods and edited main code, however this is not the main problem.
static Type CreateType(IEnumerable<PropertyInfo> properties) {
TypeBuilder typeBuilder = CreateTypeBuilder("ResultDynamicAssembly", "ResultModule", "ResultType");
foreach (PropertyInfo propertyInfo in properties) {
CreateAutoImplementedProperty(typeBuilder, propertyInfo.Name, propertyInfo.PropertyType);
}
return typeBuilder.CreateType();
}
static object CreateObject(IEnumerable<PropertyInfo> properties) {
Type type = CreateType(properties);
return Activator.CreateInstance(type);
}
You can simplify this problem by removing the dynamic type from the equation. You can reproduce the same issue with the code below, which does exactly the same thing, but without the dynamic type.
static class Program
{
private static void Main(string[] args)
{
var list = new List<Pet>();
var eList = Expression.Constant(list);
var pe = Expression.Parameter(typeof(Pet), "p");
var method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
var selectorBody = Expression.Call(method, Expression.Constant(properties));
var selector = Expression.Lambda(selectorBody, pe);
var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);
}
private static Type CreateType(IEnumerable<PropertyInfo> properties)
{
return typeof (DynamicType);
}
private static object CreateObject(IEnumerable<PropertyInfo> properties)
{
var type = CreateType(properties);
return Activator.CreateInstance(type);
}
class Pet
{
public int Id { get; set; }
public string Name { get; set; }
}
class DynamicType
{
public string Name { get; set; }
}
}
So the problem is the method signature of CreateObject. Since its return type isn't the dynamic type, the lambda isn't valid. You can see this by changing the type of CreateObject.
// this works fine
private static DynamicType CreateObject(IEnumerable<PropertyInfo> properties)
{
var type = CreateType(properties);
return (DynamicType) Activator.CreateInstance(type);
}
Since you're dealing with a dynamic type, you need to build an expression to cast the result of CreateObject to your dynamic type. Try using something like this:
// create dynamic type
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
var dynamicType = CreateType(properties);
// build expression
var list = new List<Pet>();
var eList = Expression.Constant(list);
var pe = Expression.Parameter(typeof(Pet), "p");
var createObjectMethod = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var createObjectCall = Expression.Call(createObjectMethod, Expression.Constant(properties));
var castExpression = Expression.Convert(createObjectCall, dynamicType);
var selectorExpression = Expression.Lambda(castExpression, pe);
var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), dynamicType }, eList, selectorExpression);
Create a factory for each type. You know what the source so it is really simple:
interface PetResultFactory<T>
{
string TypeName {get; }
List<T> Transform(IEnumerable<Pet> pets);
}
You have to register all possible types in container and show the registered types on the UI for the users. When a user selects one you know how to create that type becouse it is described in the factory.