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.
Related
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...
I have these lines :
var lambdaExpression = Expression.Lambda<Func<ClassOne, bool>>(eq, parameter);
var myList = db.ClassOne.Where(lambdaExpression).ToList();
I want to make those lines generic. My attempt is this:
mytype = typeof(ClassOne);
var lambdaExpression = Expression.Lambda<Func<mytype, bool>>(eq, parameter);
var myList = db.mytype.Where(lambdaExpression).ToList();
But I get :
'mytype' is a variable but is used like a type
And
'Entities' does not contain a definition for 'mytype' and no extension
method 'mytype' accepting a first argument of type 'Entities' could be
found (are you missing a using directive or an assembly reference?)
As you can see I want to pass the name of ClassOne via a variable. How can I do that? Thanks.
Given a very simple:
public class ClassOne
{
public int ID { get; set; }
}
Then we can:
public static class MyWhereHelper
{
public static readonly MethodInfo WhereMethod = typeof(MyWhereHelper).GetMethod("Where", BindingFlags.Static | BindingFlags.Public);
public static List<T> Where<T>(IQueryable<T> baseQuery, Expression<Func<T, bool>> where)
{
return baseQuery.Where(where).ToList();
}
}
and then we use it like:
// Sample data
var db = new
{
Data = new[]
{
new ClassOne { ID = 1 },
new ClassOne { ID = 2 },
new ClassOne { ID = 3 },
}.AsQueryable(),
};
var mytype = typeof(ClassOne);
// Real code begins here
var parameter = Expression.Parameter(mytype);
var eq = Expression.Equal(Expression.Property(parameter, "ID"), Expression.Constant(2));
// In truth lambdaExpression is Expression<Func<mytype, bool>>
LambdaExpression lambdaExpression = Expression.Lambda(eq, parameter);
// Search of mytype inside db
// We look for the property that is implementing IQueryable<mytype>
// We could lookup by name if the name is .mytype
var iqueryableMyType = typeof(IQueryable<>).MakeGenericType(mytype);
var prop = db.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Single(x => iqueryableMyType.IsAssignableFrom(x.PropertyType));
// db.mytype value
var propValue = prop.GetValue(db);
// MyWhereHelper.Where<mytype>
MethodInfo method = MyWhereHelper.WhereMethod.MakeGenericMethod(mytype);
var myList = (IEnumerable<object>)method.Invoke(null, new object[] { propValue, lambdaExpression });
We use reflection to find the db.mytype and for executing the query (note that we are using a "trick" here: we try to put everything that needs to use the <mytype> generic argument inside a single method, and use reflection once to execute that method. Clearly I'm speaking of MyWhereHelper.Where<>).
Inside MyWhereHelper.Where<> everything is strongly typed. Inside MyWhereHelper.Where we have a List<T> (that is a List<mytype>). But once we exit MyWhereHelper.Where<>, the caller is back again not knowing the exact type of mytype at compile time, so it can manipulate the result only as a IEnumerable<object> (or use once again reflection to manipulate it)
This is a method I always use to get a Expression<Func<type1, type2>>. This the power of reflection:
public static MethodInfo GetLambdaExpressionMethod(Type type1, Type type2)
{
return typeof(Expression).GetMethods().Where(x => x.Name == "Lambda").First()
.MakeGenericMethod(typeof(Func<,>).MakeGenericType(type1, type2));
}
You can use it, like this:
var lambda = GetLambdaExpressionMethod(yourType, typeof(bool)).Invoke(null,
new object[] {
eq,
new ParameterExpression[] { parameter }
});
Now cast the lambda variable (that is object right now) to the Expression<Func<yourType, bool>> of yourType
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);
}
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 :)
Im using Moq to create mocks of a data set.
I have created a little helper class that allows me to have an in memory storage instead of a database that makes unit testing a breeze. That way I can add and remove items from my mock data set, this allows me to test my insert and delete service calls.
During the setup of the mock I have a line that looks like the following
this.Setup(i => i.AcademicCycles).Returns(mockStore.GetList<AcademicCycle>());
My mock has a lot of properties so I would like to perform this setup step using reflection. I have managed to the Returns part of the process working via reflection but I am stuck on the lambda method to Setup.
Setup takes an
Expression<Func<GoalsModelUnitOfWork, IQueryable<AcademicCycle>>> that corresponds to the i => i.AcademicCycles
and I would like to create this dynamically. Using reflection I have the following:
The name of the property: "AcademicCycles"
The type IQueryable<AcademicCycle>
The type AcademicCycle
I also have the instance of the i in the lambda statement which is a GoalsModelUnitOfWork
The code to create the expression dynamically would be like this:
ParameterExpression parameter = Expression.Parameter(typeof (GoalsModelUnitOfWork), "i");
MemberExpression property = Expression.Property(parameter, "AcademicCycles");
var queryableType = typeof (IQueryable<>).MakeGenericType(typeof (AcademicCycle));
var delegateType = typeof (Func<,>).MakeGenericType(typeof (GoalsModelUnitOfWork), queryableType);
var yourExpression = Expression.Lambda(delegateType, property, parameter);
The result will have the desired type, but the problem is that the return type of Expression.Lambda() is LambdaExpression and you can't perform a type cast to Expression<Func<...>> to pass it as parameter to your setup function because you don't know the generic type parameters for the Func. So you have to invoke the Setup method by reflection, too:
this.GetType().GetMethod("Setup", yourExpression.GetType()).Invoke(this, yourExpression);
I decided to take a crack at it and ended up with this god awful piece of code.
I am no reflection expert and this is just a first attempt to get something working. I'd be very interested in what other approaches people have, or whether any of the relfection wrapper libraries can make this nicer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Xunit;
namespace MyExample
{
public class Tests
{
[Fact]
public void Test()
{
Dictionary<Type, object> data = new Dictionary<Type, object>
{
{ typeof(IQueryable<Cycle>), new List<Cycle> { new Cycle { Name = "Test" } }.AsQueryable() },
{ typeof(IQueryable<Rider>), new List<Rider> { new Rider { Name = "1"}, new Rider { Name = "2" } }.AsQueryable() }
};
var mock = new Mock<IDataContext>();
var setup = mock.GetType().GetMethods().Single(d => d.Name == "Setup" && d.ContainsGenericParameters);
var param = Expression.Parameter(typeof(IDataContext), "i");
foreach (var property in typeof(IDataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// Build lambda
var ex = Expression.Lambda(Expression.MakeMemberAccess(param, property), param);
// Get generic version of the Setup method
var typedSetup = setup.MakeGenericMethod(property.PropertyType);
// Run the Setup method
var returnedSetup = typedSetup.Invoke(mock, new[] { ex });
// Get generic version of IReturns interface
var iReturns = typedSetup.ReturnType.GetInterfaces().Single(d => d.Name.StartsWith("IReturns`"));
// Get the generic Returns method
var returns = iReturns.GetMethod("Returns", new Type[] { property.PropertyType });
// Run the returns method passing in our data
returns.Invoke(returnedSetup, new[] { data[property.PropertyType] });
}
Assert.Equal(1, mock.Object.Cycles.Count());
}
}
public class Cycle
{
public string Name { get; set; }
}
public class Rider
{
public string Name { get; set; }
}
public interface IDataContext
{
IQueryable<Cycle> Cycles { get; set; }
IQueryable<Rider> Riders { get; set; }
}
}
This method ought to construct the lambda expression. Since you are invoking the Setup method by reflection, you do not need a strongly-typed lambda expression; you are going to pass it as part of an object array when you call Invoke:
public LambdaExpression PropertyGetLambda(string parameterName, Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType, parameterName);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}
I don't think you actually need the parameter name. If I'm right about that, you could simplify a bit:
public LambdaExpression PropertyGetLambda(Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}