Lambda Utilities to get nested property name and value
Posted on: 2015-02-10
It is handy to develop code that does not use directly the string name of a property when creating method to work against dynamic code. In the past, we would wrote code like that:
bool isNotNull = MyMethod(myObjectToVerify, "propertyToVerify");
The problem with code that specify string method's name is that error occurs. You can mistype, you can forgot to rename the string when refactoring or you can simply not know about that string when during a replace this one got changed without you even knowing. This is why, since Microsoft .Net has introduced Lambda, you should use an expression. The result is strongly typed and it looks like this:
bool isNotNull = LambdaUtilities.VerifyNotNull( ()=>myObjectToVerify.propertyToVerify );
The code to verify the value is very simple. It consists of compiling the expression and invoking the property. If this one return null, return false, otherwise, return true. I am not going to show you how to create that simple true/false condition but how to get the value.
public static object GetValue<T>(Expression<Func<T>> selector) { var method = selector.Compile(); var value= method.Invoke(); return value; }
The method works for nested property. So you can use these variants when using it:
var x = GetValue(()=>yourObject.Property); var x2 = GetValue(()=>yourObject.Property.NestedProperty); var x3 = GetValue(()=>yourObject.Property.NestedProperty.AndSoOn);
Getting the name of the property require more code. The code come from one of my project and if my memory is good, come almost all from a StackOverflow post.
public static string GetPropertyName<T>(Expression<Func<T>> expression) { var stack = new Stack<string>(); Expression expression1 = expression.Body; while (expression1 != null) { if (expression1.NodeType == ExpressionType.Call) { var methodCallExpression = (MethodCallExpression)expression1; if (IsSingleArgumentIndexer(methodCallExpression)) { stack.Push(string.Empty); expression1 = methodCallExpression.Object; } else break; } else if (expression1.NodeType == ExpressionType.ArrayIndex) { var binaryExpression = (BinaryExpression)expression1; stack.Push(string.Empty); expression1 = binaryExpression.Left; } else if (expression1.NodeType == ExpressionType.MemberAccess) { var memberExpression = (MemberExpression)expression1; stack.Push("." + memberExpression.Member.Name); expression1 = memberExpression.Expression; } else if (expression1.NodeType == ExpressionType.Parameter) { stack.Push(string.Empty); expression1 = null; } else if (expression1.NodeType == ExpressionType.Convert) { var memberExp = ((UnaryExpression)expression1).Operand as MemberExpression; stack.Push("." + memberExp.Member.Name); expression1 = memberExp.Expression; } else break; } if (stack.Count > 0 && string.Equals(stack.Peek(), ".model", StringComparison.OrdinalIgnoreCase)) stack.Pop(); if (stack.Count <= 0) return string.Empty; return (stack).Aggregate(((left, right) => left + right)).TrimStart(new[] { '.' }); }
private static bool IsSingleArgumentIndexer(Expression expression) { var methodExpression = expression as MethodCallExpression; if (methodExpression == null || methodExpression.Arguments.Count != 1) return false; return (methodExpression.Method.DeclaringType.GetDefaultMembers()).OfType<PropertyInfo>().Any((p => p.GetGetMethod() == methodExpression.Method)); }
To get the name of the property, you have to call it like the one to get the value. This one support as well nested properties.
var x = GetPropertyName(()=>yourObject.Property); var x2 = GetPropertyName(()=>yourObject.Property.NestedProperty); var x3 = GetPropertyName(()=>yourObject.Property.NestedProperty.AndSoOn);
With these two methods, you should be in business to get the value and the name of properties from Lambda. Avoiding the use of string is interesting for your maintainability and it allows you to modify without fearing to forget a string somewhere.