Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Get Nested Property value using reflection and Linq.Expression

20 Nov 2011CPOL2 min read 60.7K  
Get Nested Property value using reflection and Linq.Expression
Click here to download the assembly from NuGet

Sometimes, we need to access an object's nested properties. To avoid the very common "Object Reference Null" exception, we have to test all the nodes of the object tree, which makes the code less readable.

Let's see an example to clarify what I'm try to achieve. Let's say, I got Employ and Address classes which are implemented as below:

C#
public class Employee
{
    public string Name { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
    public Employee Director { get; set; }
}    
public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
    public string PostCode { get; set; }
}


Now suppose we want to get the Director's city property from his employee, it would look like something below:

C#
if(employee.Director != null && employee.Director.Address !=null)
{
    employee.Director.Address.City;
}


Using the extension methods, we are allowed to access the property without testing all the "nodes" as showed below:

C#
string city = employee.NullSafeGetValue(x => x.Director.Address.City, "NoCity");


As you can see, the NullSafeGetValue takes two parameters:

The first parameter is a lambda expression of the property we want to access.

The second is a default value, which is returned when the property is not reachable (due to perhaps the Director member being null).

I have found lots of solutions on the internet, but all of them were using expression trees which add lots of overhead.

My solution still uses “lambda expression” syntax, but it doesn’t use any expression tree.

Below is the NullSafeGetValue extension method code:

C#
using System;
using System.Collections;
using System.Linq.Expressions;
using System.Reflection;
public static class ObjectExtension
{
     /// <summary>
    ///
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <param name="source">Root Object - it must be a reference type or a sub class of IEnumerable</param>
    /// <param name="expression">Labmda expression to set the property value returned</param>
    /// <param name="defaultValue">The default value in the case the property is not reachable </param>
    /// <returns></returns>
    public static TResult NullSafeGetValue<TSource, TResult>(this TSource source, Expression<Func<TSource, TResult>> expression, TResult defaultValue)
    {
        var value = GetValue(expression, source);
        return value == null ? defaultValue : (TResult)value;
    }
    /// <summary>
    ///
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <typeparam name="TResult"></typeparam>
    /// <typeparam name="TCastResultType"></typeparam>
    /// <param name="source">Root Object</param>
    /// <param name="expression">Labmda expression to set the property value returned</param>
    /// <param name="defaultValue">The default value in the case the property is not reachable</param>
    /// <param name="convertToResultToAction">An action to cast the returned value</param>
    /// <returns></returns>
    public static TCastResultType NullSafeGetValue<TSource, TResult, TCastResultType>(this TSource source, Expression<Func<TSource, TResult>> expression, TCastResultType defaultValue, Func<object, TCastResultType> convertToResultToAction)
    {
        var value = GetValue(expression, source);
        return value == null ? defaultValue : convertToResultToAction.Invoke(value);
    }
    private static string GetFullPropertyPathName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
    {
        return expression.Body.ToString().Replace(expression.Parameters[0] + ".", string.Empty);
    }
    private static object GetValue<TSource, TResult>(Expression<Func<TSource, TResult>> expression, TSource source)
    {
        string fullPropertyPathName = GetFullPropertyPathName(expression);
        return GetNestedPropertyValue(fullPropertyPathName, source);
    }
    private static object GetNestedPropertyValue(string name, object obj)
    {
        PropertyInfo info;
        foreach (var part in name.Split('.'))
        {
            if (obj == null)
            {
                return null;
            }
            var type = obj.GetType();
            if (obj is IEnumerable)
            {
                type = (obj as IEnumerable).GetType();
                var methodInfo = type.GetMethod("get_Item");
                var index = int.Parse(part.Split('(')[1].Replace(")", string.Empty));
                try
                {
                    obj = methodInfo.Invoke(obj, new object[] { index });
                }
                catch (Exception)
                {
                    obj = null;
                }
            }
            else
            {
                info = type.GetProperty(part);
                if (info == null)
                {
                    return null;
                }
                obj = info.GetValue(obj, null);
            }
        }
        return obj;
    }
}


There is also an overload that allows you to cast the property before it is returned.

Just to give you an example, we need to add a property to Address class named “HouseNumber”:

C#
public class Address
{
      public string City { get; set; }
      public string Street { get; set; }
      public string PostCode { get; set; }
      public string HouseNumber { get; set; }
}


Now let’s suppose we want to access the employee’s director address to get the house number and cast it to an int. We would do something like the below:

Using the NullSafeGetValue overload, we can pass a function for casting the property before it is returned:

C#
int houseNumber = employ.NullSafeGetValue(x => x.Director.Address.HouseNumber, 0, x => int.Parse(x.ToString()));


The extension method does work even with IEnumerable objects (as List)

Let's change a bit the address class making address a list of address:

C#
public class Employ
   {
       public string Name { get; set; }
       public string LastName { get; set; }
       public IList<Address> Addresses { get; set; }
       public Employ Director { get; set; }
   }


Now, let's say I want to access the address[0].street:

I can do the below:

C#
var street = e.NullSafeGetValue(x => x.Addresses[0].Street, string.Empty);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)