Implementation of method accepting lambda - c#

I'm interested to understand how are implemented LabelFor, EditorFor... methods that accept lambda expressions in MVC.
Lets say I have a class Person and I want to print the name and the value of a property. How must be implemented Label() and Editor() methods?
class Person
{
public int Id { get; set; }
}
void Label(Expression<Func<Person, int>> expression)
{
//...
}
void Editor(Expression<Func<Person, int>> expression)
{
//...
}
public void Test()
{
Person p = new Person
{
Id = 42
};
Label(x => x.Id ); // print "Id"
Editor(x => x.Id); // print "42"
}

This answer to a similar question gives an implementation of Label. The code's by Josh Smith in the PropertyObserver class of his MVVM foundation:
private static string GetPropertyName
(Expression<Func<TPropertySource, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
Debug.Assert(memberExpression != null,
"Please provide a lambda expression like 'n => n.PropertyName'");
if (memberExpression != null)
{
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.Name;
}
return null;
}
It works by looking at the expression tree and checking for the name of the property in member expressions.
For Editor, you can use a similar strategy of looking through the Expression to find out what you need about the property. What exactly to do depends a lot on what info you want.
The specific way you asked it where all you want is the value of a property from a Person, you can simplify a lot. I also added a Person parameter, since you seem to want the value for a given person.
int Editor(Person person, Func<Person, int> expression)
{
return expression(person);
}
This can be used like Editor(p, p => p.Id);. Note that I changed Expression<Func<Person, int>> to Func<Person, int>, which means that instead of an expression tree it gets a Func. You can't examine a Func to find names of properties and such, but you can still use it to find the property from the Person.

void Label(Expression<Func<Person, int>> expression)
{
Person myPerson = new Person { Id = 42 };
foreach (PropertyInfo prop in typeof(Person).GetProperties())
if(prop.Equals(expression(myPerson))) print(prop.Name);
}
void Editor(Expression<Func<Person, int>> expression)
{
Person myPerson = new Person { Id = 42 };
print(expression(myPerson));
}

Related

How extract list values from expression

I need to read the values from one array, that is provided as an expression.
I have this method
public ExpressionAnalizer<TModel> where TModel : class
{
public string BuildExpression(Expression<Func<TModel, bool>> expression)
{
if (expression?.Body is MethodCallExpression)
return BuildMethodCallExpression(expression);
throw new ArgumentException($"The expression '{expression?.Body}' is unsupported");
}
public string BuildMethodCallExpression(Expression<Func<TModel, bool>> expression)
{
var body = expression.Body as MethodCallExpression;
//TODO: I can't find a property that has the values of IEnumerable
return null;
}
}
And is called like this
//PersonModel is a plain class with some properties.
var analizer= new ExpressionAnalizer<PersonModel>("");
var names = new List<string>() {"n1", "n2", "n3"};
//I want to get "Email contains ('n1', 'n2', 'n3')". I can read Email property, the call method name "Contains", but not itself values
var response = analizer.BuildExpression(x => names.Contains(x.Email));
Any idea? I was thinking to compile the expression, but I get stuck on "Closure" class, because System.Runtime.CompilerServices.Closure is private and I cannot use it.
By the way, I'm using .NET Core 1.0
EDIT
I need to get a string like
email contains ('n1','n2','n3')
And the input parameter need to be always an Expression
Expression<Func<TModel, bool>>
This is because internally, this method can receive any expression, like
x => x.SomeProperty == "n1"
Internally, I handle the expression.Body type and I have an implementation for different use cases.
The case I cannot figure how can I implement is when the input expression is
var someList = new List<string>() { "string1", "anotherString", "finalString" };
someObject.SomeProperty<SomeTModel>(x => someList.Contains(x.SomeProperty))
Here is the full implementation of your ExpressionAnalizer class which will give you the desired output.
public class ExpressionAnalizer<TModel> where TModel : class
{
public string BuildExpression(Expression<Func<TModel, bool>> expression)
{
if (expression?.Body is MethodCallExpression)
return BuildMethodCallExpression(expression);
throw new ArgumentException($"The expression '{expression?.Body}' is unsupported");
}
public string BuildMethodCallExpression(Expression<Func<TModel, bool>> expression)
{
var body = expression.Body as MethodCallExpression;
//Get Method Name
string method = body.Method.Name;
//Get List of String Values
var methodExpression = ResolveMemberExpression(body.Object);
var listValues = ReadValue(methodExpression);
var vString = string.Format("'{0}'", string.Join("' , '", (listValues as List<string>)));
//Read Propery Name
var argExpression = ResolveMemberExpression(body.Arguments[0]);
var propertyName = argExpression.Member.Name;
return $"{propertyName} {method} ({vString})";
}
public MemberExpression ResolveMemberExpression(Expression expression)
{
if (expression is MemberExpression) return (MemberExpression)expression;
if (expression is UnaryExpression) return (MemberExpression)((UnaryExpression)expression).Operand;
throw new NotSupportedException(expression.ToString());
}
private object ReadValue(MemberExpression expression)
{
if (expression.Expression is ConstantExpression)
{
return (((ConstantExpression)expression.Expression).Value)
.GetType()
.GetField(expression.Member.Name)
.GetValue(((ConstantExpression)expression.Expression).Value);
}
if (expression.Expression is MemberExpression) return ReadValue((MemberExpression)expression.Expression);
throw new NotSupportedException(expression.ToString());
}
}
Usage:
var analizer= new ExpressionAnalizer<PersonModel>();
var names = new List<string>() {"n1", "n2", "n3"};
var person = new PersonModel{ Email = "Email 1"};
var response = analizer.BuildExpression( x => names.Contains(x.Email));
Response:
Email Contains ('n1' , 'n2' , 'n3')

Is there a cleaner way to set Properties of Generic Classes?

Here's what I have so far, it works fine. Im just wondering if there is a smoother way to go about it:
public static PropertyInfo GetProperty<T, T2>(this Expression<Func<T, T2>> selectorExpression)
{
var memberExpression = selectorExpression.Body as MemberExpression;
if (memberExpression == null) throw new InvalidCastException();
return memberExpression.Member as PropertyInfo;
}
Here's an example function that can use it now. This one will set all selected values of objects in a list to something.
public static List<T> Set<T,T2>(this List<T> inList, decimal amount, Expression<Func<T, decimal>> valueSelector)
where T : class
{
var valueProperty = valueSelector.GetProperty();
foreach (var item in inList)
{
valueProperty.SetValue(item, amount);
}
return inList
}
Then I can simply just do this:
myList.Set(100, i => i.Value);
Where Value is some Setter property of the objects in MyList.
Now I know that second function is a super simple example. I am actually using GetProperty for way more complex stuff, specifically I have written a function that divies up a value amongst an IEnumerable to a selected setter property, based on a Getter 'weight' property in it.
The main thing I want to be discussing is my GetProperty function itself. Is there a better way to go about this or am I on the right track here already? Any kinds of further null checking or something I should be doing?
Just because the question was tagged C#-7.0, I thought of offering an answer with C#-7.0 features:
public static PropertyInfo GetProperty<TObject, TProperty>(
this Expression<Func<TObject, TProperty>> selectorExpression)
=> selectorExpression.Body is MemberExpression memberExpression
&& memberExpression.Member is PropertyInfo propertyInfo
? propertyInfo
: throw new InvalidCastException();
This works for me:
public static PropertyInfo GetProperty<T>(this Expression<Func<T, decimal>> selectorExpression)
{
var memberExpression = selectorExpression.Body as MemberExpression;
if (memberExpression == null) throw new InvalidCastException();
return memberExpression.Member as PropertyInfo;
}
Then, with this code, I get 42 written to the console:
void Main()
{
Expression<Func<Foo, decimal>> exp = q => q.Bar;
var p = exp.GetProperty();
var f = new Foo();
p.SetValue(f, 42m);
Console.WriteLine(f.Bar);
}
public class Foo
{
public decimal Bar { get; set; }
}

Dynamically create lambda search on IQueryable on nested objects

I'm trying to build a dynamic search on nested objects, which will later be sent to EF and SQL Server. So far, I'm able to search on all properties of the first object. Here's a very simplified version:
public class User
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
}
public class MyClass<TEntity> where TEntity : class {
public IQueryable<TEntity> applySearch(IQueryable<TEntity> originalList, string propName, string valueToSearch) {
ParameterExpression parameterExpression = Expression.Parameter(typeof(TEntity), "p");
PropertyInfo propertyInfo = typeof(TEntity).GetProperty(propName);
MemberExpression member = Expression.MakeMemberAccess(parameterExpression, propertyInfo);
lambda = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(member, Expression.Constant(valueToSearch)), parameterExpression);
return originalList.Where(expression);
}
}
When propName = "Name" everything is fine, but when propName = "Address.City", the propertyInfo is null, and I get this error on the member assignment line:
System.ArgumentNullException: Value cannot be null
I was able to obtain the propertyInfo of the nested property using the solution from this answer:
PropertyInfo propertyInfo = GetPropertyRecursive(typeof(TEntity), propName);
...
private PropertyInfo GetPropertyRecursive(Type baseType, string propertyName)
{
string[] parts = propertyName.Split('.');
return (parts.Length > 1)
? GetPropertyRecursive(baseType.GetProperty(parts[0]).PropertyType, parts.Skip(1).Aggregate((a, i) => a + "." + i))
: baseType.GetProperty(propertyName);
}
But then I get this error on member assignment:
System.ArgumentException: Property 'System.String City' is not defined for type 'User'
This should point to Address instead of User, but I don't know if I'm on right track here, I mean, should I change parameterExpression now?
How can I make a dynamic search on nested objects, so that this can be turned into a lambda expression and later sent to SQL?
After Kobi's advice and a lot of trial and error, I finally got this working. This uses the Universal PredicateBuilder. Here it is:
public class MyClass<TEntity> where TEntity : class
{
public IQueryable<TEntity> ApplySearch(IQueryable<TEntity> originalList, string valueToSearch, string[] columnsToSearch)
{
Expression<Func<TEntity, bool>> expression = null;
foreach (var propName in columnsToSearch)
{
Expression<Func<TEntity, bool>> lambda = null;
ParameterExpression parameterExpression = Expression.Parameter(typeof(TEntity), "p");
string[] nestedProperties = propName.Split('.');
Expression member = parameterExpression;
foreach (string prop in nestedProperties)
{
member = Expression.PropertyOrField(member, prop);
}
var canConvert = CanConvertToType(valueToSearch, member.Type.FullName);
if (canConvert)
{
var value = ConvertToType(valueToSearch, member.Type.FullName);
if (member.Type.Name == "String")
{
ConstantExpression constant = Expression.Constant(value);
MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
Expression call = Expression.Call(member, mi, constant);
lambda = Expression.Lambda<Func<TEntity, bool>>(call, parameterExpression);
}
else
{
lambda = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(member, Expression.Constant(value)), parameterExpression);
}
}
if (lambda != null)
{
if (expression == null)
{
expression = lambda;
}
else
{
expression = expression.Or(lambda);
}
}
}
if (expression != null)
{
return originalList.Where(expression);
}
return originalList;
}
}
private bool CanConvertToType(object value, string type)
{
bool canConvert;
try
{
var cValue = ConvertToType(value, type);
canConvert = true;
}
catch
{
canConvert = false;
}
return canConvert;
}
private dynamic ConvertToType(object value, string type)
{
return Convert.ChangeType(value, Type.GetType(type));
}
Warning in advance - I'm not building the expression, just inspecting its structure.
When I need to dynamically create Expressions, I find it useful to inspect an Expression and copy its structure:
Expression<Func<User, string>> getCity = user => user.Address.City;
Now you can simply debug it, for example in the immediate window (ctrlalti here):
getCity
{user => user.Address.City}
Body: {user.Address.City}
CanReduce: false
DebugView: ".Lambda #Lambda1<System.Func`2[ConsoleApplication1.User,System.String]>(ConsoleApplication1.User $user) {\r\n ($user.Address).City\r\n}"
Name: null
NodeType: Lambda
Parameters: Count = 1
ReturnType: {Name = "String" FullName = "System.String"}
TailCall: false
Here we can see getCity is a Lambda with one parameter. Let's inspect it's body:
getCity.Body
{user.Address.City}
CanReduce: false
DebugView: "($user.Address).City"
Expression: {user.Address}
Member: {System.String City}
NodeType: MemberAccess
Type: {Name = "String" FullName = "System.String"}
getCity.Body is a member access - it accesses the member City of the Expression user.Address. Technically that's a PropertyExpression, which is an internal class so we can't even cast to it, but that's OK.
Finally, let's look at that inner expression:
((MemberExpression)getCity.Body).Expression
{user.Address}
CanReduce: false
DebugView: "$user.Address"
Expression: {user}
Member: {ConsoleApplication1.Address Address}
NodeType: MemberAccess
Type: {Name = "Address" FullName = "ConsoleApplication1.Address"}
That's just user.Address.
Now we can build an identical expression:
var addressProperty = typeof (User).GetProperty("Address");
var cityProperty = typeof(Address).GetProperty("City");
var userParameter = Expression.Parameter(typeof (User), "user");
var getCityFromUserParameter = Expression.Property(Expression.Property(userParameter, addressProperty), cityProperty);
var lambdaGetCity = Expression.Lambda<Func<User, string>>(getCityFromUserParameter, userParameter);
Expression.MakeMemberAccess works too, instead of Expression.Property.
Obviously, you'd need to build your expression in a loop, and more dynamically, but the structure is the same.
It might be worth taking a look at Linqkit's predicate builder...
http://www.albahari.com/nutshell/predicatebuilder.aspx
I'd also take a look at Entity SQL...
https://msdn.microsoft.com/en-us/library/vstudio/bb387145(v=vs.100).aspx
You might be reinventing a wheel with the code you're writing.
Also I should comment, in terms of the SQL Server plan caching, unless you have no other choice I wouldn't dynamically build queries. You're better off creating a single query that handles all your cases that SQL Server can cache a plan for, your queries will run a lot slower if every time they're executed no plan is hit in SQL Server's plan cache.

Generic order by parameter

I'm trying to do a generic method that would accept an order by parameter that I could then inspect for it's name and attributes before building the query and sending it to the database.
I was thinking of something like:
public class SearchEngine<T>
{
// Removed other parameters to make it simple
public IEnumerable<T> Search<K>(Func<T, K> orderBy)
{
throw new NotImplementedException();
}
}
I was hoping to consume it later with:
var searchEngine = new SearchEngine<User>();
var result = searchEngine.Search(x => x.Name);
My goal is to get a hold, inside of the search method, of that property so that I could get the name of the property, "Name" in this case, and even after it, use reflection to get its attributes to get other information I have setup.
I know about getting the attributes, what I'm not sure is how can I get the info of the property being passed. I'm sure Linq does this in some way, I just don't know how.
If I tried something like:
var result = searchEngine.Search<PropertyInfo>(x => x.Name);
This wouldnt work since the parameter is returning a string in this case.
Any ideas would be appreciated.
Use expression tree and change Search method parameter type to Expression<Func<T, K>>:
public IEnumerable<T> Search<K>(Expression<Func<T, K>> orderBy)
{
var memberExpression = orderBy.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("orderBy");
var member = memberExpression.Member;
var memberName = member.Name;
return null;
}
It will throw ArgumentException when orderBy is not simple, member expression.
You can still call the method the same way:
var result = searchEngine.Search(x => x.Name);
Change parameter type of Search to:
Expression<Func<T, K>>
and try like this:
public class SearchEngine<T>
{
// Removed other parameters to make it simple
public IEnumerable<T> Search<K>(Expression<Func<T, K>> orderBy)
{
var name = GetMemberName(orderBy.Body);
throw new NotImplementedException();
}
static string GetMemberName(Expression expression)
{
string memberName = null;
var memberExpression = expression as MemberExpression;
if (memberExpression != null)
memberName = memberExpression.Member.Name;
var unaryExpression = expression as UnaryExpression;
if (unaryExpression != null)
memberName = ((MemberExpression) unaryExpression.Operand).Member.Name;
var methodCallExpression = expression as MethodCallExpression;
if (methodCallExpression != null)
memberName = methodCallExpression.Method.Name;
Contract.Assume(memberName != null);
return memberName;
}
}
You can use Dynamic Linq. The syntax would be something like:
string criteria = "Age < 40 and Age > 30";
string sort = "Name";
var result = searchEngine.Where(criteria).OrderBy(sort);

Expression for Type members results in different Expressions (MemberExpression, UnaryExpression)

Description
I have a expression to point on a property of my type.
But it does not work for every property type. "Does not mean" means
it result in different expression types. I thought it will ever result in a
MemberExpression but this is not the case.
For int and Guid it results in a UnaryExpression and for string
in a MemberExpression.
I am a little confused ;)
Some sample code
My class
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Test Code
Person p = new Person { Age = 16, Name = "John" };
Expression<Func<Person, object>> expression1 = x => x.Age;
// expression1.Body = UnaryExpression;
Expression<Func<Person, object>> expression2 = x => x.Name;
// expression2.Body = MemberExpression;
Question
How can i compare two expressions and check if they are mean
the same type and same property ?
Update, Answer and complete Sample
Thanks to user dasblinkenlight who brought me on the right track.
He provided the method
private static MemberExpression GetMemberExpression<T>(
Expression<Func<T,object>> exp
) {
var member = expr.Body as MemberExpression;
var unary = expr.Body as UnaryExpression;
return member ?? (unary != null ? unary.Operand as MemberExpression : null);
}
I wrote the following extension method to compare the results of the GetMemberExpression
methods and check if GetMemberExpression().Member.Name are the same.
private static bool IsSameMember<T>(this Expression<Func<T, object>> expr1, Expression<Func<T, object>> expr2)
{
var result1 = GetMemberExpression(expr1);
var result2 = GetMemberExpression(expr2);
if (result1 == null || result2 == null)
return false;
return result1.Member.Name == result2.Member.Name;
}
The reason this happens is that Age is a value type. In order to coerce an expression returning a value type into Func<Person,object> the compiler needs to insert a Convert(expr, typeof(object)), a UnaryExpression.
For strings and other reference types, however, there is no need to box, so a "straight" member expression is returned.
If you would like to get to the MemberExpression inside the UnaryExpression, you can get its operand:
private static MemberExpression GetMemberExpression<T>(
Expression<Func<T,object>> exp
) {
var member = exp.Body as MemberExpression;
var unary = exp.Body as UnaryExpression;
return member ?? (unary != null ? unary.Operand as MemberExpression : null);
}
Rather than comparing the Member.Name string, I would suggest comparing the PropertyInfo instances directly for equality, in order to avoid false positives when two properties in distinct classes share the same name.
public static bool IsSameProperty<TSourceA, TSourceB, TPropertyA, TPropertyB>(
Expression<Func<TSourceA, TPropertyA>> expA,
Expression<Func<TSourceB, TPropertyB>> expB)
{
MemberExpression memExpA = expA.Body as MemberExpression;
MemberExpression memExpB = expB.Body as MemberExpression;
if (memExpA == null || memExpB == null)
return false;
PropertyInfo propA = memExpA.Member as PropertyInfo;
PropertyInfo propB = memExpB.Member as PropertyInfo;
if (propA == null || propB == null)
return false;
return propA.Equals(propB);
}
You can ensure that your lambda expression is compiled as a MemberExpression rather than a UnaryExpression simply by specifying the correct value type (rather than object) as the generic type TResult of your Expression<Func<T, TResult>> expression.
Expression<Func<Person, int>> expression1 = x => x.Age;
Expression<Func<Person, int>> expression2 = x => x.Age;
Expression<Func<Person, string>> expression3 = x => x.Name;
Console.WriteLine(IsSameProperty(expression1, expression2)); // True
Console.WriteLine(IsSameProperty(expression1, expression3)); // False

Categories

Resources