All posts in Deep Null

In Effective Extensions – Extending Enums in C# we covered how to use a few useful methods to extend Enums in C#. This time we are going to look at Extending Models in C#.

UPDATED 8/29/2014: Added a few more GetPropertyName overloads as well as GetPropertyNames.

Our first method is our initial GetPropertyName which retrieves a property name from a simple expression:


/// <summary>
/// Gets the property name from an expression.
/// </summary>
/// <param name="expression">The expression to evaluate.</param>
/// <returns></returns>
public static string GetPropertyName(this Expression expression)
{
    // Get property name
    switch (expression.NodeType)
    {
        case ExpressionType.Call:
            var callExp = expression as MethodCallExpression;
            if (callExp == null || !callExp.Arguments.Any()) return "";

            // Build body on first argument
            var callBody = callExp.Arguments.First() as MemberExpression;

            // Get property name
            return callBody == null ? "" : callBody.Member.Name;
            break;

        case ExpressionType.AndAlso:
        case ExpressionType.OrElse:
            var andOrExp = expression as BinaryExpression;
            if (andOrExp == null) return "";

            // Build body on last argument
            var andOrBody = andOrExp.Right as MemberExpression;

            // Get property name
            return andOrBody == null ? "" : andOrBody.Member.Name;
            break;

        default:
            // Member
            var body = expression as MemberExpression;

            // Body found
            if (body != null) return body.Member.Name;

            // Get body
            var ubody = (UnaryExpression)expression;
            body = ubody.Operand as MemberExpression;
            return (body != null) ? body.Member.Name : "";
            break;
    }
}

The goal of GetPropertyName is to find the relevant MemberExpression and get the property name associated with it. This is not always a simple task and different Expression types have to be evaluated in unique ways.

Let’s break this down into each case:

  1. The first block deals with a MethodCallExpression. You typically see these with expressions like this:
    x => x.MyCollection.Contains(5). We need to mine this down to the first argument to get the MemberExpression.
  2. The second and third blocks deal with complex expressions using && (AndAlso) or || (OrElse) between 2 or more statements. An example might be like this: x => x.IsActive && x.MyValue == 5. The Right value will hold the right-most, singular value as a MemberExpression, where the Left value will hold the expression less the Right. So, to properly take an expression like this apart, you would need to iterate through until Left is just a MemberExpression.
  3. The last block is a simple MemberExpression evaluation. When all else fails, go this route. Sometimes, casting our expression as a MemberExpression won’t work, so we attempt to cast it as a UnaryExpression and cast it’s Operand property as a MemberExpression.

 

Now, let’s look at some GetPropertyName overloads that will use our initial method to evaluate complex expressions:


/// <summary>
/// Gets the property name from an expression.
/// Example: this would be used for expressions in the format: "() => Name"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression to evaluate.</param>
/// <returns></returns>
public static string GetPropertyName<T>(this Expression<Func<T>> expression)
{
    return expression.Body.GetPropertyName();
}

/// <summary>
/// Gets the property name from an expression.
/// Example: this would be used for expressions in the format: "() => IsActive"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
/// <returns></returns>
public static string GetPropertyName<T>(this Expression<Func<T, bool>> expression)
{
    return expression.Body.GetPropertyName();
}

/// <summary>
/// Gets the property name from an expression.
/// Example: this would be used for expressions in the format: "x => x.Name"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="expression">The exp.</param>
/// <returns></returns>
public static string GetPropertyName<T, TProperty>(this Expression<Func<T, TProperty>> expression)
{
    return expression.Body.GetPropertyName();
}

  • The first method retrieves a property name from a simple expression such as: () => Name.
  • The second method retrieves a property name from a boolean expression such as: () => IsActive.
  • The third method retrieves a property name from a property-based expression such as: x => x.Name.

 

Q: Okay, so how do we get all the property names of a multi-statement expression that uses && or ||?
A: In order to do that, we need to do a little more work and return an array.

Let’s look at our follow-up method GetPropertyNames:


/// <summary>
/// Gets multiple property names from a complex expression.
/// Example: this would be used for expressions in the format: "x => x.Name &amp;&amp; x.IsActive"
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
/// <returns></returns>
public static IList<string> GetPropertyNames<T>(this Expression<Func<T, bool>> expression)
{
    var names = new List<string>();

    // Body
    var expBody = expression.Body;

    // Get property name
    if (expBody is BinaryExpression)
    {
        // If a complex expression, we need to disassemble it from Right to Left,
        // Or, we simply need to get the property from the Left.
        if (expBody.IsComplexBinaryExpression())
        {
            names.AddRange(expBody.GetPropertiesFromBinary());
        }
        else
        {
            names.Add(expBody.GetPropertyNameFromSimpleBinaryExpression());
        }
    }

    else
    {
        // Get a singular property name
        names.Add(expression.GetPropertyName());
    }

    // Return unique list
    return names.GroupBy(x => x).Select(y => y.Key).ToList();
}

/// <summary>
/// Gets the properties from binary.
/// </summary>
/// <param name="expression">The expression.</param>
/// <returns></returns>
private static IEnumerable<string> GetPropertiesFromBinary(this Expression expression)
{
    var names = new List<string>();

    var currentExp = expression;
    while (currentExp is BinaryExpression)
    {
        // Cast expression
        var exp = (currentExp as BinaryExpression);

        // Body
        if (exp.IsComplexBinaryExpression())
        {
            // If "Right" is a BinaryExpression, get it's components, or
            // Just get the property name of the "Right" expression.
            var right = exp.Right as BinaryExpression;
            if (right != null)
            {
                names.AddRange(right.GetPropertiesFromBinary());
            }
            else
            {
                names.Add(exp.Right.GetPropertyName());
            }
        }

        else
        {
            // Add to list
            names.Add(exp.GetPropertyNameFromSimpleBinaryExpression());
        }

        // Next expression
        currentExp = exp.Left;
    }

    // Get last expression
    names.Add(currentExp.GetPropertyName());

    // Return
    return names;
}

/// <summary>
/// Determines whether [is complex binary expression] [the specified exp].
/// </summary>
/// <param name="exp">The exp.</param>
/// <returns></returns>
private static bool IsComplexBinaryExpression(this Expression exp)
{
    var expBody = exp as BinaryExpression;
    if (expBody == null) return false;

    // Evaluate
    return (expBody.NodeType == ExpressionType.AndAlso || expBody.NodeType == ExpressionType.OrElse);
}

/// <summary>
/// Gets the property name from simple binary expression.
/// </summary>
/// <param name="exp">The exp.</param>
/// <returns></returns>
private static string GetPropertyNameFromSimpleBinaryExpression(this Expression exp)
{
    var expBody = exp as BinaryExpression;
    if (expBody == null) return "";

    // Body
    var body = expBody.Left as MemberExpression;

    // Get property name
    return (body != null) ? body.Member.Name : "";
}

Here, we are doing what we discussed earlier, which is iterating through our expression, storing the Right value on each pass until the Left is just a MemberExpression. The result is a list of all properties involved in the expression.
The next method we will evaluate is DeepCopy which completely clones a model through serialization:


/// <summary>
/// Deeps the copy.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="oSource">The o source.</param>
/// <returns></returns>
public static T DeepCopy<T>(this T oSource)
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, oSource);
        ms.Position = 0;
        return (T)formatter.Deserialize(ms);
    }
}

As you can see, this serializes the source with a BinaryFormatter and injects it into a MemoryStream. This is considered the most effective way to clone an object.

 

Finally, we have an IsNull method that performs a Deep Null check, which means it inspects each element in an object tree to determine if it is valid:


/// <summary>
/// Checks if property or field and all parent instances if they are null.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent">The parent.</param>
/// <param name="lambda">The lambda expression.</param>
/// <returns><c>true</c> if null; otherwise, <c>false</c>.</returns>
/// <remarks>Only properties and/or fields are allowed for all parts of the expression.</remarks>
public static bool IsNull<T>(this object parent, Expression<Func<T>> lambda)
{
    var memberParts = new List<MemberInfo>();
    var expression = lambda.Body;

    // ExpressionType.Constant occurs once we get to the parent
    while (expression.NodeType != ExpressionType.Constant)
    {
        var memberExpression = (MemberExpression)expression;
        memberParts.Add(memberExpression.Member);
        expression = memberExpression.Expression;
    }

    // Parts are extracted in reverse order, so this corrects it
    memberParts.Reverse();

    // 'instance' keeps track of the instance associated with 'member' below
    var instance = parent;
    while (memberParts.Any())
    {
        // Set the current member for evaluation on this loop
        var member = memberParts.First();

        // Break down how this is evaluated by property vs. field
        switch (member.MemberType)
        {
            case MemberTypes.Property:
                var property = (PropertyInfo)member;

                // Gets the value by invoking the property on the instance
                object value = property.GetValue(instance, null);

                // Compares value with null
                if (value == null) return true;

                // If not null, set 'instance' to the value for the next loop
                instance = value;
                break;

            case MemberTypes.Field:
                var field = (FieldInfo)member;

                // Gets the value by v the field on the instance
                value = field.GetValue(instance);

                // Compares value with null
                if (value == null) return true;

                // If not null, set 'instance' to the value for the next loop
                instance = value;
                break;

            default:
                // Method type members, and others, are not supported
                throw new InvalidOperationException("IsNull: MemberType must be Property or Field.");
        }

        // If not null, remove the first element for the next loop
        memberParts.RemoveAt(0);
    }

    // No nulls were found
    return false;
}

As demonstrated, this method inspects each element from greatest parent down until reaching our target element to ensure all elements in the tree are not null.

The usage would be like this:


// Why do this:
var test = (parent == null || parent.MyProperty == null || parent.MyProperty.ChildProperty == null);

// When you can do this:
var test = parent.IsNull(()=> parent.MyProperty.ChildProperty);

// Check
// "test" is true if parent is null.

 

And that wraps this series on extensions.

Happy coding.