i'm starting to explore dynamic expressions, so please help me to solve one issue.
I have an object
public class Categorisation{
string Name{get;set;}
}
public class Client{
public Categorisation Categorisation{get;set;}
}
All I need is to write a dynamic expression and call Categorisation.Name.Equals("A1") from Client object.
x=>x.Categorisation.Name.Equals("A1")
How can I do this using Expressions?
var param = Expression.Parameter(typeof(Client));
var prop = Expression.Property(param, typeof(Client).GetProperty("Categorisation"));
var argument = Expression.Constant("A1");
var method = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var call = Expression.Call(prop, method);
var expr = Expression.Lambda<Func<Client, bool>>(call, param);
Of course this code is wrong and I call method Equals from Categorisation property and not from Name of Categorisation. But how to invoke Name property?
var param = Expression.Parameter(typeof(Client));
var prop = Expression.Property(param, typeof(Client).GetProperty("Categorisation"));
var namePropExpr = Expression.Property(prop, "Name");
var argument = Expression.Constant("A1");
var method = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var call = Expression.Call(namePropExpr, method, argument);
var expr = Expression.Lambda<Func<Client, bool>>(call, param);
Related
I try to make an expression with a PropertyInfo. The expression should look like:
Expression < Func < PropertyType >>
I need this Expression for this:
https://github.com/aspnet/AspNetCore/blob/8d46b3a64ea784c95dddeb9d421c7cda6de993a2/src/Components/Web/src/Forms/ValidationMessage.cs
The For property needs this kind of Expression.
Can anyone direct me to some links how to get this kind of Expression?
var entity = new Entity;
var propertyInfo = entity.GetType().GetProperty("OneProperty");
var expr = GetExpression(propertyInfo);
// could be:
// Expression<Func<string>> or Expression<Func<int>>
edited;
If you need a property accessor for a property name given as string, you must also pass it the the object as parameter. Therefore you need an
Expression<Func<Entity, PropertyType>>
This function creates such an expression:
private static LambdaExpression CreatePropertyGetterExpression(Type entityType,
string propertyName)
{
PropertyInfo property = entityType.GetProperty(propertyName);
var parameter = Expression.Parameter(entityType, "e");
var body = Expression.MakeMemberAccess(parameter, property);
return Expression.Lambda(body, parameter);
}
This overload allows you to pass the entity type as generic parameter:
private static LambdaExpression CreatePropertyGetterExpression<TEntity>(string propertyName)
{
return CreatePropertyGetterExpression(typeof(TEntity), propertyName);
}
This test
var expr = CreatePropertyGetterExpression<Entity>("OneProperty");
Console.WriteLine(expr);
Console.WriteLine(expr.GetType());
prints (assuming OneProperty to be of type int):
e => e.OneProperty
System.Linq.Expressions.Expression`1[System.Func`2[MyProject.Entity, System.Int32]]
You can get an Expression<Func<PropertyType>> if you capture a variable like this:
Entity e = new Entity { PropertyOne = 42 };
Expression<Func<int>> expr = () => e.PropertyOne;
But how do you want to capture a variable if you are constructing an expression dynamically? This is not possible.
I found this HtmlHelperValidationExtensions.ValidationMessageFor Method doing what you need:
public static IHtmlContent ValidationMessageFor<TModel,TResult> (
this IHtmlHelper<TModel> htmlHelper,
Expression<Func<TModel,TResult>> expression,
string message,
object htmlAttributes);
You can pass it the Expression<Func<TModel,TResult>> expression from above! However, you must know the result type in advance. Otherwise you will have to use reflection to call it.
I found a solution while trying to bind blazor inputs dynamically:
Entity e = new Entity { PropertyOne = 42 };
Expression<Func<int>> expr = () => e.PropertyOne;
to get the expression for this statement you should use a constant expression:
var e = new Entity() { PropertyOne = 42 };
var property = typeof(Entity).GetProperty("PropertyOne");
var entityType = typeof(Entity);
var parameter = Expression.Constant(e);
var body = Expression.MakeMemberAccess(parameter, property);
var resultToReturn = Expression.Lambda<func<TValue>>(body);
best regards.
I'm trying to create a dynamic lambda exp to filter some results on my list, but I can't figure out on how to create a dynamic func<,>
//Here I get the type of my object "Produto", but I can have other objects here...Like "Pedido" or another one.
Type type = typeof(Produto);
var param = Expression.Parameter(type, "arg");
var propName = Expression.Property(param, "Codigo");
var constP = Expression.Constant("123");
var nameStartsWith = Expression.Call(
propName,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
constP);
//Here I have to instantiate my Func<T, bool>
//I can't instantiate it with my variable "type" to use on my Where clausule.
//I don't know how to do this in another way, to create my lambda.
var WhereExp = Expression.Lambda<Func<type, bool>>(nameStartsWith, param);
return _uow.produto.GetAll().AsQueryable().Where(WhereExp).ToList();
You need to create a generic method. Something like this (untested):
public Expression<Func<T, bool>> DynamicWhere<T>(string pname, string value) where T : class
{
var param = Expression.Parameter(typeof(T), "arg");
var propName = Expression.Property(param, pname);
var constP = Expression.Constant(value);
var nameStartsWith = Expression.Call(
propName,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
constP);
return Expression.Lambda<Func<T, bool>>(nameStartsWith, param);
}
Then you can use it like this:
var WhereExp = DynamicWhere<Produto>("Codigo", "123");
return _uow.produto.GetAll().AsQueryable().Where(WhereExp).ToList();
I want to create a Lambda Expression for every Property of an Object that reads the value dynamically.
What I have so far:
var properties = typeof (TType).GetProperties().Where(p => p.CanRead);
foreach (var propertyInfo in properties)
{
var getterMethodInfo = propertyInfo.GetGetMethod();
var entity = Expression.Parameter(typeof (TType));
var getterCall = Expression.Call(entity, getterMethodInfo);
var lambda = Expression.Lambda(getterCall, entity);
var expression = (Expression<Func<TType, "TypeOfProperty">>) lambda;
var functionThatGetsValue = expression.Compile();
}
The code Works well when i call functionThatGetsValue as long as "TypeOfProperty" is hardcoded. I know that I can't pass the "TypeOfPoperty" dynamically. What can I do to achive my goal?
Assuming that you're happy with a Func<TType, object> delegate (as per the comments above), you can use Expression.Convert to achieve that:
var properties = typeof(TType).GetProperties().Where(p => p.CanRead);
foreach (var propertyInfo in properties)
{
MethodInfo getterMethodInfo = propertyInfo.GetGetMethod();
ParameterExpression entity = Expression.Parameter(typeof(TType));
MethodCallExpression getterCall = Expression.Call(entity, getterMethodInfo);
UnaryExpression castToObject = Expression.Convert(getterCall, typeof(object));
LambdaExpression lambda = Expression.Lambda(castToObject, entity);
var functionThatGetsValue = (Func<TType, object>)lambda.Compile();
}
After hours of googling found the answer here. I've added the snippets from the blog post as it might help others having the same troubles:
public static class PropertyInfoExtensions
{
public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
return (Func<T, object>)Expression.Lambda(convert, instance).Compile();
}
public static Action<T, object> GetValueSetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var argument = Expression.Parameter(typeof(object), "a");
var setterCall = Expression.Call(
instance,
propertyInfo.GetSetMethod(),
Expression.Convert(argument, propertyInfo.PropertyType));
return (Action<T, object>)Expression.Lambda(setterCall, instance, argument).Compile();
}
}
I've modified gsharp's post above to actually set the value directly and make it a bit easier to use. It's not ideal as there is the introduction of the DynamicCast function which requires you to know your type up front. My goal was to try to keep us strongly typed and not return object and avoid dynamic keyword. Also, keep "magic" to a minimum.
public static T DynamicCast<T>(this object value)
{
return (T) value;
}
public static object GetPropertyValue<T>(this PropertyInfo propertyInfo, T objectInstance)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, propertyInfo.PropertyType);
var lambda = Expression.Lambda(convert, instance).Compile();
var result = lambda.DynamicInvoke(objectInstance);
return result;
}
public static void SetPropertyValue<T, TP>(this PropertyInfo propertyInfo, T objectInstance, TP value)
where T : class
where TP : class
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var argument = Expression.Parameter(propertyInfo.PropertyType, "a");
var setterCall = Expression.Call(
instance,
propertyInfo.GetSetMethod(),
Expression.Convert(argument, propertyInfo.PropertyType));
var lambda = Expression.Lambda(setterCall, instance, argument).Compile();
lambda.DynamicInvoke(objectInstance, value);
}
Examples:
public void Get_Value_Of_Property()
{
var testObject = new ReflectedType
{
AReferenceType_No_Attributes = new object(),
Int32WithRange1_10 = 5,
String_Requires = "Test String"
};
var result = testObject.GetType().GetProperty("String_Requires").GetPropertyValue(testObject).DynamicCast<string>();
result.Should().Be(testObject.String_Requires);
}
public void Set_Value_Of_Property()
{
var testObject = new ReflectedType
{
AReferenceType_No_Attributes = new object(),
Int32WithRange1_10 = 5,
String_Requires = "Test String"
};
testObject.GetType().GetProperty("String_Requires").SetPropertyValue(testObject, "MAGIC");
testObject.String_Requires.Should().Be("MAGIC");
}
You could write a helper method which uses MakeGenericMethod or an expression tree to make a lambda do the typed call to call DynamicCast based on the PropertyInfo object and avoid having to know it up front. But that is less elegant.
I am attempting to create a dynamic query using expression trees to match the following statement:
var items = data.Where(i => i.CoverageType == 2).Select(i => i.LimitSelected);
I can create the where method and get a result from it; however, I cannot create the select method.
Here is my where method:
var parm = Expression.Parameter(typeof(BaseClassData), "baseCoverage");
var queryData = data.AsQueryable();
var left = Expression.Property(parm, "CoverageType");
var right = Expression.Constant(2m);
var e1 = Expression.Equal(left, right);
var whereMethod = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryData.ElementType },
queryData.Expression,
Expression.Lambda<Func<BaseClassData, bool>>(e1, new ParameterExpression[] { parm }));
This is what I am using for the select method:
var selectParm = Expression.Property(parm, "LimitSelected");
var selectMethod = Expression.Call(
typeof(Enumerable),
"Select",
new Type[]{typeof(BaseClassData), typeof(decimal)},
whereMethod,
Expression.Lambda<Func<BaseClassData, decimal>>(selectParm, new ParameterExpression[]{ parm})
);
When I run the code I get this error:
No generic method 'Select' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
I have also tried changing Enumerable to Queryable and I get the same error.
No need to use Expression.Call, you can directly construct Expression Tree instead; I have create a static method which help me generate dynamic query:
public static void Test(string[] args) {
using (var db = new DBContext()) {
//query 1
var query1 = db.PrizeTypes.Where(m => m.rewards == 1000).Select(t => t.name);
//query 2 which equal to query 1
Expression<Func<PrizeType, bool>> predicate1 = m => m.rewards == 1000;
Expression<Func<PrizeType, string>> selector1 = t => t.name;
var query2 = db.PrizeTypes.Where(predicate1).Select(selector1);
Console.WriteLine(predicate1);
Console.WriteLine(selector1);
Console.WriteLine();
//query 3 which equal to query 1 and 2
Expression<Func<PrizeType, bool>> predicate2 = GetPredicateEqual<PrizeType>("rewards", (Int16)1000);
Expression<Func<PrizeType, string>> selector2 = GetSelector<PrizeType, string>("name");
var query3 = db.PrizeTypes.Where(predicate2).Select(selector2);
Console.WriteLine(predicate2);
Console.WriteLine(selector2);
//as you can see, query 1 will equal query 2 equal query 3
}
}
public static Expression<Func<TEntity, bool>> GetPredicateEqual<TEntity>(string fieldName, object fieldValue) where TEntity : class {
ParameterExpression m = Expression.Parameter(typeof(TEntity), "t");
var p = m.Type.GetProperty(fieldName);
BinaryExpression body = Expression.Equal(
Expression.Property(m, fieldName),
Expression.Constant(fieldValue, p.PropertyType)
);
return Expression.Lambda<Func<TEntity, bool>>(body, m);
}
public static Expression<Func<T, TReturn>> GetSelector<T, TReturn>(string fieldName)
where T : class
where TReturn : class {
var t = typeof(TReturn);
ParameterExpression p = Expression.Parameter(typeof(T), "t");
var body = Expression.Property(p, fieldName);
return Expression.Lambda<Func<T, TReturn>>(body, new ParameterExpression[] { p });
}
This might help with the error you describe above.
You don't need to create your own where/select however, the ones built into c#/linq work just fine with your own classes:
void Main()
{
List<testdata> data = new List<testdata>();
Directory.GetFiles(#"C:\").ToList().ForEach(x=>data.Add(new testdata(){file=x,returnable=1}));
data.Where(x=>x.file.Contains("g")).Select(x=>x.file).Dump();
}
class testdata
{
public string file {get; set;}
public string returnable {get; set;}
}
I want to create a factory that will create commonly mocked objects for my unit tests. I've already managed to set up my tests so I can mock up a Linq2Sql DataContext and return an in memory table instead of hitting the database. I set it up like this:
_contactsTable = new InMemoryTable<Contact>(new List<Contact>());
_contactEmailsTable = new InMemoryTable<ContactEmail>(new List<ContactEmail>());
// repeat this for each table in the ContactsDataContext
var mockContext = new Mock<ContactsDataContext>();
mockContext.Setup(c => c.Contacts).Returns(_contactsTable);
mockContext.Setup(c => c.ContactEmails).Returns(_contactEmailsTable);
// repeat this for each table in the ContactsDataContext
This gets tedious if the DataContext contains a lot of tables, so I thought a simple factory method that used reflection to get all the tables off the DataContext might help:
public static DataContext GetMockContext(Type contextType)
{
var instance = new Mock<DataContext>();
var propertyInfos = contextType.GetProperties();
foreach (var table in propertyInfos)
{
//I'm only worried about ITable<> now, otherwise skip it
if ((!table.PropertyType.IsGenericType) ||
table.PropertyType.GetGenericTypeDefinition() != typeof (ITable<>)) continue;
//Determine the generic type of the ITable<>
var TableType = GetTableType(table);
//Create a List<T> of that type
var emptyList = CreateGeneric(typeof (List<>), TableType);
//Create my InMemoryTable<T> of that type
var inMemoryTable = CreateGeneric(typeof (InMemoryTable<>), TableType, emptyList);
//NOW SETUP MOCK TO RETURN THAT TABLE
//How do I call instance.Setup(i=>i.THEPROPERTYNAME).Returns(inMemoryTable) ??
}
return instance.Object;
}
So far I've figured out how to create the objects I need to setup for the Mock, but I just can't figure out how to dynamically call Moq's Setup() passing in the property names. I started looking at reflection to Invoke() Moq's Setup() method, but it got really ugly fast.
Does anyone have a simple way to dynamically call Setup() and Returns() like this?
Edit: Brian's answer got me there. Here's how it works:
public static DataContext GetMockContext<T>() where T: DataContext
{
Type contextType = typeof (T);
var instance = new Mock<T>();
var propertyInfos = contextType.GetProperties();
foreach (var table in propertyInfos)
{
//I'm only worried about ITable<> now, otherwise skip it
if ((!table.PropertyType.IsGenericType) ||
table.PropertyType.GetGenericTypeDefinition() != typeof(ITable<>)) continue;
//Determine the generic type of the ITable<>
var TableType = GetTableType(table);
//Create a List<T> of that type
var emptyList = CreateGeneric(typeof(List<>), TableType);
//Create my InMemoryTable<T> of that type
var inMemoryTable = CreateGeneric(typeof(InMemoryTable<>), TableType, emptyList);
//NOW SETUP MOCK TO RETURN THAT TABLE
var parameter = Expression.Parameter(contextType);
var body = Expression.PropertyOrField(parameter, table.Name);
var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter);
instance.Setup(lambdaExpression).Returns(inMemoryTable);
}
return instance.Object;
}
What you are looking for are Linq Expressions. Here is a sample of building a property accessory expression in action.
Using this class:
public class ExampleClass
{
public virtual string ExampleProperty
{
get;
set;
}
public virtual List<object> ExampleListProperty
{
get;
set;
}
}
The following tests demonstrate accessing its properties dynamically using the Linq.Expression classes.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void SetupDynamicStringProperty()
{
var dynamicMock = new Mock<ExampleClass>();
//Class type
var parameter = Expression.Parameter( typeof( ExampleClass ) );
//String rep of property
var body = Expression.PropertyOrField( parameter, "ExampleProperty" );
//build the lambda for the setup method
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
dynamicMock.Setup( lambdaExpression ).Returns( "Works!" );
Assert.AreEqual( "Works!", dynamicMock.Object.ExampleProperty );
}
[TestMethod]
public void SetupDynamicListProperty_IntFirstInList()
{
var dynamicMock = new Mock<ExampleClass>();
var parameter = Expression.Parameter( typeof( ExampleClass ) );
var body = Expression.PropertyOrField( parameter, "ExampleListProperty" );
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
var listOfItems = new List<object> { 1, "two", DateTime.MinValue };
dynamicMock.Setup( lambdaExpression ).Returns( listOfItems );
Assert.AreEqual( typeof( int ), dynamicMock.Object.ExampleListProperty[0].GetType() );
Assert.AreEqual( 1, dynamicMock.Object.ExampleListProperty[0] );
Assert.AreEqual( 3, dynamicMock.Object.ExampleListProperty.Count );
}
[TestMethod]
public void SetupDynamicListProperty_StringSecondInList()
{
var dynamicMock = new Mock<ExampleClass>();
var parameter = Expression.Parameter( typeof( ExampleClass ) );
var body = Expression.PropertyOrField( parameter, "ExampleListProperty" );
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
var listOfItems = new List<object> { 1, "two" };
dynamicMock.Setup( lambdaExpression ).Returns( listOfItems );
Assert.AreEqual( typeof( string ), dynamicMock.Object.ExampleListProperty[1].GetType() );
Assert.AreEqual( "two", dynamicMock.Object.ExampleListProperty[1] );
Assert.AreEqual( 2, dynamicMock.Object.ExampleListProperty.Count );
}
}
EDIT
You are taking a step too far with this code. This code is creating a method with the signature of the lambda you want and then it's executing it (.Invoke). You are then trying to pass the result of the object (hence the compile error) into the setup for Moq. Moq will do the method execution and hookup for you once you tell it how to act (hence the lambda). If you use the lambda expression creation that I provided, it will build what you need.
var funcType = typeof (Func<>).MakeGenericType(new Type[] {TableType, typeof(object)});
var lambdaMethod = typeof (Expression).GetMethod("Lambda");
var lambdaGenericMethod = lambdaMethod.MakeGenericMethod(funcType);
var lambdaExpression = lambdaGenericMethod.Invoke(body, parameter);
//var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>(body, parameter); // FOR REFERENCE FROM BRIAN'S CODE
instance.Setup(lambdaExpression).Returns(inMemoryTable);
Do this instead
var parameter = Expression.Parameter( TableType );
var body = Expression.PropertyOrField( parameter, "PutYourPropertyHere" );
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
instance.Setup(lambdaExpression).Returns(inMemoryTable);
EDIT
Took a stab at correcting the GetMockContext. Please note the few changes (I marked each line). I think this is closer. I am wondering, does InMemoryTable inherit from DataContext? If not, the method signature will be incorrect.
public static object GetMockContext<T>() where T: DataContext
{
Type contextType = typeof (T);
var instance = new Mock<T>(); //Updated this line
var propertyInfos = contextType.GetProperties();
foreach (var table in propertyInfos)
{
//I'm only worried about ITable<> now, otherwise skip it
if ((!table.PropertyType.IsGenericType) ||
table.PropertyType.GetGenericTypeDefinition() != typeof(ITable<>)) continue;
//Determine the generic type of the ITable<>
var TableType = GetTableType(table);
//Create a List<T> of that type
var emptyList = CreateGeneric(typeof(List<>), TableType);
//Create my InMemoryTable<T> of that type
var inMemoryTable = CreateGeneric(typeof(InMemoryTable<>), TableType, emptyList);
//NOW SETUP MOCK TO RETURN THAT TABLE
var parameter = Expression.Parameter(contextType);
var body = Expression.PropertyOrField(parameter, table.Name);
var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter);
instance.Setup(lambdaExpression).Returns(inMemoryTable);
}
return instance.Object; //had to change the method signature because the inMemoryTable is not of type DataContext. Unless InMemoryTable inherits from DataContext?
}
I hope this helps!