I am trying to dynamically configure Moq using reflection as per Dynamically calling Moq Setup() at runtime
In the example they use
var body = Expression.PropertyOrField( parameter, "ExampleProperty" );
To create an expression to select the desired property on the object and then construct a lambda expression using that selector.
I would like my selector to select a method instead, eg I want to dynamically construct the following:
mock.Setup(m => m.MyMethod()).Returns(1);
I have tried using:
var body = Expression.PropertyOrField(parameter, "MyMethod");
and
var body = Expression.MakeMemberAccess(parameter, typeof(T).GetMethod("MyMethod"));
but both seem to only work on properties or fields, is there a different selector I can use to select a method instead?
Full code below:
var mock = new Mock<T>();
var parameter = Expression.Parameter(typeof(T));
if (typeof(T).GetMethod("MyMethod") != null)
{
var body = Expression.PropertyOrField(parameter, "MyMethod");
var lambdaExpression = Expression.Lambda<Func<T, int>>(body, parameter);
mock.Setup(lambdaExpression).Returns(0);
}
While I don't have ready access to Moq here, I can show you how to build a lambda expression which invokes a method
private void DoIt<T>()
{
var mock = new Mock<T>();
var parameter = Expression.Parameter(typeof(T));
var methodInfo = typeof(T).GetMethod("MyMethod"); //Find the method "MyMethod" on type "T"
if (methodInfo != null)
{
var body = Expression.Call(parameter, methodInfo);
var lambdaExpression = Expression.Lambda<Func<T, int>>(body, parameter);
//At this point, lambdaExpression is:
//Param_0 => Param_0.MyMethod()
mock.Setup(lambdaExpression).Returns(0);
}
}
class MyClass
{
public int MyMethod()
{
return 5;
}
}
Note that typeof(T).GetMethod("MyMethod") is not very specific. There are plenty of overloads you can use (or leverage GetMethods and filter) that will allow you to specify the method by name, return type and parameter types.
Also, be aware the Expression.Lambda<Func<T, int>> will only work for methods with no parameters which return an int. Depending on who's responsible for determining the method, you may want to have this configurable, too.
Related
My goal is to support sorting in an application and expose it via REST API that would accept the parameter as a string.
Current implementation is along the lines of this:
GetUsers (Expression<Func<User, int>> sortExpression) {
// Retrieve users ...
return users.orderBy(sortExpression);
}
Usage example:
var users = GetUsers(u => u.Id);
the Expression<Func<User, int>> sortExpression is widely used in our repository and changing it would be difficult.
What I'd like to do is to be able to swap the u => u.Id with something that is generated during run-time.
Something that I have so far is:
// sortBy is retrieved from the calling method.
var type = typeof(User).GetProperties().FirstOrDefault(x => x.Name == sortBy).GetType();
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
var users = GetUsers(expressionBody)
I can see at run-time that this does create an expression that fits my needs, but the error is Argument 5: cannot convert from 'LambdaExpression' to 'Expression<System.Func<User, int>>' even though the body of the expression is supposed to be set by typeof(Func<User, int>)
I've figured out what I've been doing wrong.
First: Create the expression body using generic method
// Generic Method, return type Expression<Func<User, int>>
Expression.Lambda<Func<User, int>>(sortExpression, parameter);
Instead of passing the typeof(Func<User, int>) parameter.
// Non-generic. Return type LambdaExpression
Expression.Lambda(typeof(Func<User, int>), sortExpression, parameter);
Second:
I wasn't binding the parameter properly, which made it so that the expression was accessing property of a discarded parameter that wasn't provided to the expression.
// I'm creating an expression to access the property of a newly created parameter.
var sortExpression = Expression.Property(Expression.Parameter(typeof(User)), sortBy);
var parameter = Expression.Parameter(typeof(User));
var expressionBody = Expression.Lambda<Func<User, int>>(sortExpression, parameter);
//Above causes an unbinded variable exception since there are two parameters, one of which is not passed/bound.
//Should be:
var parameter = Expression.Parameter(typeof(User));
var sortExpression = Expression.Property(parameter, sortBy);
I have a method to insert entity to tables genericly using type T. But I want to add a feature to check if the entity to be added exists in the table based on a matcher property (e.g Name). When I execute the code it throws 'has no supported translation to SQL' on the check line. My code part is below. How can I fix this problem?
public static InsertStatus Add<T>(T ent, string matcherProp) where T : class
{
System.Data.Linq.Table<T> t = otdc.GetTable<T>();
//Exception on this line
if (t.Any(item => object.Equals(GetPropValue(item, matcherProp), GetPropValue(ent, matcherProp))))
return InsertStatus.AlreadyExists;
try
{
t.InsertOnSubmit(ent);
otdc.SubmitChanges();
return InsertStatus.Successfull;
}
catch
{
return InsertStatus.UnknownError;
}
}
public static object GetPropValue(object src, string propName)
{
return src.GetType().GetProperty(propName).GetValue(src, null);
}
You will need to create an expresion tree at runtime. Fortunately this isn't very hard in your case; it will be something like:
var p = Expression.Parameter(typeof(T), "p");
var val = GetPropValue(ent, matcherProp);
var test = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.PropertyOrField(p, matcherProp),
Expression.Constant(val)
), p);
if (t.Any(test))
return InsertStatus.AlreadyExists;
What this does is construct the logical tree for:
p => p.{matcherProp} == {val}
where matcherProp is the name of the member to test, and val is the existing value as a constant.
Note that you might get issues if val is null, unless you can also supply the type of the property (.PropertyType on the PropertyInfo) - and supply it to Expression.Constant.
Edit: another way to do this is to supply ent as the constant:
var p = Expression.Parameter(typeof(T), "p");
var test = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.PropertyOrField(p, matcherProp),
Expression.PropertyOrField(Expression.Constant(ent), matcherProp),
), p);
This is then more akin to:
p => p.{matcherProp} == ent.{matcherProp}
where ent in the lambda behaves much like a "captured variable".
Instead of making matcherProp a string, consider making it an Expression<Func<T, P>> so you can invoke it as: Add(myEntity, e => e.Name).
Then you need something like
public static InsertStatus Add<T>(T ent, Expression<Func<T,P>> keySelector) where P : class
{
System.Data.Linq.Table<T> t = otdc.GetTable<T>();
var memberAccess = (keySelector as LambdaExpression)?.Body as MemberExpression;
ParameterExpression paramExpr = Expression.Parameter(typeof(T), "e");
Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T,bool>>(
Expression.Equal(memberAccess.Update(Expression.Constant(ent)), memberAccess.Update(paramExpr)), paramExpr);
if (t.Any(predicate))
{
With appropriate error checking of course.
I'm trying to build an dynamic expression from a string of property-names (given by the user) on an IQueryable named source.
This is what I have so far:
var parameter = Expression.Parameter(source.ElementType, "x");
var member = propertyChain.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);
var selector = Expression.Lambda(member, parameter);
which will give me something like x => x.MainProperty.SubProperty when the input would be MainProperty.SubProperty.
I now need to add ToString() to the expression selector so it will produce the expression x => x.MainProperty.SubProperty.ToString() which then can be passed into other methods.
How can this be done?
Edit 1
I'm trying to build a dynamic GroupBy where the type of the key doesn't matter. But the property to group by can be of type Guid, int or something else. That's why I need to call ToString().
public static IEnumerable<IGrouping<string, T>>(IQueryable<T> source, string propertyChain)
{
var parameter = Expression.Parameter(source.ElementType, "x");
var member = propertyChain.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);
var selector = Expression.Lambda(member, parameter);
// currently here I have x => x.MainProperty.SubProperty
// here I would Invoke the GroupBy of IQueryable with T and string via reflection
}
You can use the following Expression.Call overload for instance:
var toString = Expression.Call(member, "ToString", Type.EmptyTypes);
var selector = Expression.Lambda<Func<T, string>>(toString, parameter);
return source.GroupBy(selector);
I have a Group by expression that I am dynamically creating for use in a LINQ query. Currently, to construct the expression, I use the following code:
var arg = Expression.Parameter(typeof(T), helper.getName());
var prop = Expression.Property(arg, "customerType");
var body = Expression.Convert(prop, typeof(object));
var lambda = Expression.Lambda<Func<Contact, object>>(body, arg);
var keySelector = lambda.Compile();
I then use the keySelector in the GroupBy for my LINQ query. My question is, if I wanted to add a second grouping criteria to this expression, say "salesStage", how would I add that to this existing expression?
You have a problem, because what the compiler does on a regular GroupBy call is generate a new anonymous type with the properties you define. If the type doesn't exist, we cannot create an expression creating an object of the type.
However, given that you are using this for LINQ-to-Objects, we can use the Tuple<> type to generate the grouping key. Hopefully you do not need to group on more than 8 parameters.
Here is a generic function to generate the grouping function:
static Func<T, object> BuildGrouper<T>(IEnumerable<string> properties) {
var arg = Expression.Parameter(typeof(T), helper.getName());
// This is the list of property accesses we will be using
var parameters = properties.Select(propName => Expression.Property(arg, propName)).ToList();
// Find the correct overload of Tuple.Create.
// This will throw if the number of parameters is more than 8!
var method = typeof(Tuple).GetMethods().Where(m => m.Name == "Create" && m.GetParameters().Length == parameters.Count).Single();
// But it is a generic method, we need to specify the types of each of the arguments
var paramTypes = parameters.Select(p => p.Type).ToArray();
method = method.MakeGenericMethod(paramTypes);
// Invoke the Tuple.Create method and return the Func
var call = Expression.Call(null, method, parameters);
var lambda = Expression.Lambda<Func<T, object>>(call, arg);
return lambda.Compile();
}
Consider this:
var propertyinfo = typeof(Customer).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;
now I want to declare
Func<int,orderType>
I know its not possible directly since ordertype is at runtime but is there is any workaround ?
this is exactly what I want to do :
var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;
var param = Expression.Parameter(typeof(T), "x");
var sortExpression = (Expression.Lambda<Func<T, orderType>>
(Expression.Convert(Expression.Property(param, sortExpressionStr), typeof(orderType)), param));
all this because I want to convert:
Expression<Func<T,object>> to Expression<Func<T,orderType>>
or if its not possible then I want to create it from the first place with the right type , the case is as following:
I'm inside a method which have a type(Customer) and a property name of that type I want to order by it , I want to create a sort expression tree to pass it to Orderby (here).
You can do this by using an open generic type definition, and then making the specific type from that:
typeof(Func<,>).MakeGenericType(typeof(int), orderType);
However, what you're trying to do (calling Lambda<TDelegate>) is not directly possible. You must call Lambda without a type parameter:
var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;
var param = Expression.Parameter(typeof(T), "x");
var sortExpression = Expression.Lambda(
Expression.Convert(Expression.Property(param, sortExpressionStr),
orderType),
param));
This will create the proper Func<,> for you behind the scenes. If you want to compile the expression and use the delegate, you can only do this dynamically with
sortExpression.Compile().DynamicInvoke(param);
If you want to call the OrderBy extension method on Queryable, things get a little more complicated:
var propertyInfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyInfo.PropertyType;
// first find the OrderBy method with no types specified
MethodInfo method = typeof(Queryable).GetMethods()
.Where(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
.Single();
// then make the right version by supplying the right types
MethodInfo concreteMethod = method.MakeGenericMethod(typeof(T), orderType);
var param = Expression.Parameter(typeof(T), "x");
// the key selector for the OrderBy method
Expression orderBy =
Expression.Lambda(
Expression.Property(orderParam, propertyInfo),
orderParam);
// how to use:
var sequence = new T[0].AsQueryable(); // sample IQueryable
// because no types are known in advance, we need to call Invoke
// through relection here
IQueryable result = (IQueryable) concreteMethod.Invoke(
null, // = static
new object[] { sequence, orderBy });
You can use the Type.MakeGenericType Method:
Type result = typeof(Func<,>).MakeGenericType(typeof(int), orderType);
This should work:
public static IQueryable<T> OrderByField<T>(
IQueryable<T> q, string sortfield, bool ascending)
{
var p = Expression.Parameter(typeof(T), "p");
var x = Expression.Lambda(Expression.Property(p, sortfield), p);
return q.Provider.CreateQuery<T>(
Expression.Call(typeof(Queryable),
ascending ? "OrderBy" : "OrderByDescending",
new Type[] { q.ElementType, x.Body.Type },
q.Expression,
x));
}
From here.
linqClass.OrderBy(GetSortExpression(sortstr));
public static Expression<Func<T,object>> GetSortExpression<T>(string sortExpressionStr)
{
var param = Expression.Parameter(typeof(T), "x");
var sortExpression = Expression.Lambda<Func<T, object>>(Expression.Property(param, sortExpressionStr), param);
return sortExpression;
}
this worked my problem was i used to pass extra parameter Typeof(Object) and orderby used to tell me it cant sort by Object type. thanks all
thanks dtb i will check if your answer work too and i will accept it if it works if not i will accept thsi one.
You want to use Dynamic Linq, a part of the Visual Studio sample code.
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
See if my solution dynamic enough.
public class Product
{
public long ID { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
}
static void Main(string[] args)
{
List<Product> products = (from i in Enumerable.Range(1, 10)
select new Product { ID = i, Name = "product " + i, Date = DateTime.Now.AddDays(-i) }).ToList(); //the test case
const string SortBy = "Date"; // to test you can change to "ID"/"Name"
Type sortType = typeof(Product).GetProperty(SortBy).PropertyType; // DateTime
ParameterExpression sortParamExp = Expression.Parameter(typeof(Product), "p"); // {p}
Expression sortBodyExp = Expression.PropertyOrField(sortParamExp, SortBy); // {p.DateTime}
LambdaExpression sortExp = Expression.Lambda(sortBodyExp, sortParamExp); // {p=>p.DateTime}
var OrderByMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("OrderBy") && m.GetParameters().Count() == 2).FirstOrDefault().MakeGenericMethod(typeof(Product), sortType);
var result = OrderByMethod.Invoke(products, new object[] { products, sortExp.Compile() });
}
Base on above, it's not difficult to change Product to T to make it generic.
You can get the Type associated with Func<int,orderType> in case you wanted to e.g. pass it into CreateDelegate.
But what are you ultimately wanting to do with it? There may be a more straightforward approach.