Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#

Dynamically Check Nested Values for IsNull Values

Rate me:
Please Sign up or sign in to vote.
4.78/5 (8 votes)
22 Aug 2014CPOL5 min read 34K   14   19
Dynamically check a value for Null value in an expression tree with an extension method

Introduction

This is based on something I found on stack overflow (after a whole day's search). This is something that I needed a lot, especially with DevExpress. So if you have many methods/classes in a tree expression, instead of checking each individual value for Null before you get the value, I found a way to check if any value is null and if not, to return the value you are looking for.

Background

This is very helpful in Devexpress as I said but in general in C# also very helpful and also in MVC.

Using the Code

Let's say you have a Class called Contracts. In this contracts class, you have all the bookings (Booking Class) connected to the class. In the Bookings class, you have the people's details (Person Class). In that, you have the persons Security clearance with his Username to login to the system (Security Class). So let's say I want to get the username of the person connected to the contract.

Normally, you will need to get the first value and then the next and next, etc. as follows...

C#
If (Contracts != null && Contracts.Booking != null && 
Contracts.Booking.Person != null && Contracts.Booking.Person.Security != null)
{
    string personUsername = Contracts.Booking.Person.Security.Username;
} 

Now this can become a drag, especially if you have a multitude of information on which all have different values from different classes. It also takes a lot of effort if something in the code changes or a class changes.

Now what I found to work is to create an extension class in the project that is globally accessible. So I choose common Functions as a place. The method that will be used is a C# built in function called ExpressionVisitor. I am going to call my class IsNullVisitor.

The only namespaces required to do this will be the following:

C#
using System;
using System.Linq.Expressions;
using System.Reflection; 

For this example, I will only need 2 classes. One that extends ExpressionVisitor and then a static class I called helper. In my helper class, I have 4 static objects to do my testing.

So let us start with the ExpressionVisitor class. Mine looks as follows:

C#
public class IsNullVisitor : ExpressionVisitor
{
    public bool IsNull { get; private set; }
    public object CurrentObject { get; set; }
    
    protected override Expression VisitMember(MemberExpression node)
    {
        base.VisitMember(node);
        if (CheckNull())
            return node;

        var member = (PropertyInfo)node.Member;
        CurrentObject = member.GetValue(CurrentObject, null); 
        CheckNull();
        return node;
    }
    
    private bool CheckNull()
    {
        if (CurrentObject == null)
            IsNull = true;

        return IsNull;
    }
} 

With this, we will use the helpers constructed in Linq expressions to test each 'level' of the classes until a value can be found. But if any one of the objects comes back Null, it will return null value.

Now to do this, I will show you the first object I used. This object takes any object and tests whether it has any null object, if it does not, it will return the object value. I will use a linq expression to check each individual object and if any one is null, return null value as follows. Take note that all the objects will be under the Helper class I created:

C#
public static class Helper
{
    // Static objects here...
} 

In this class, I create the isNull checks required. First being the object that tests and just returns the object as is or the value null.

C#
public static object IsNullValueReturn<T>
(this T root, Expression<Func<T, object>> getter)
{
    var visitor = new IsNullVisitor();
    visitor.CurrentObject = root;
    visitor.Visit(getter);
    If (visitor.IsNull)
        return null;

    return visitor.CurrentObject;
}  

So let us take the above example and see how we will query this...

C#
string personUsername = Contracts.IsNullStringReturn(c => c.Booking.Person.Security.Username); 

This will now test until a null is found. Thus it will test Contracts and if not null, it tests Contracts.Booking and if not null, keeps testing until it finds a null. It will continue to iterate through the MemberExpression until one returns a value true. Thus it will get the base.Visit member which will be Contracts in this case and keep testing until a Null is found. So it tests the base.VisitMember(MemberExpression) for null and returns if true. If false, it gets the PropertyInfo of the MemberExpression's member and test if the Value can be got if not it is null. It then tests for the second time the Null value and if not null, it continues on (with the 'tree') until a null value causes it to return or null was not found in which case the final value tested's object can be returned. So in example if nothing tested null, it returns the Username value.

The second test I am going to do is one where I specifically wants a string value to return in the case where the object tested is not a string value (ToString() method). Since string can be Null, I can still return the value as Null if null values were found.

C#
public static string IsNullStringReturn<T>(this T root, Expression<Func<T, object>> getter)
{
    var visitor = new IsNullVisitor();
    visitor.CurrentObject = root;
    visitor.Visit(getter);
    if (visitor.IsNull)
        return null;

    return visitor.CurrentObject.ToString();
} 

The third one I will show is to return just the 1st letter or if any null value, the string 'N/A'.

C#
public static string IsNullNACharReturn<T>(this T root, Expression<Func<T, object>> getter)
{
    var visitor = new IsNullVisitor();
    visitor.CurrentObject = root;
    visitor.Visit(getter);
    if (visitor.IsNull)
        return "N/A";

    return Format.FirstChar(visitor.CurrentObject.ToString());
} 

The code used in my firstChar method is as below:

C#
public static string FirstChar(string item)
{
    if (!String.IsNullOrEmpty(item))
        if (item.Trim().Length > 0)
            return item.Trim().Substring(0,1);

    return string.Empty;
}  

This might return a blank value but still you will be able to test if null or string.Empty for accurate calculations, etc.

The last one will be testing and returning a bool value.

C#
public static bool? IsNullBoolReturn<T>(this T root, Expression<Func<T, object>> getter)
{
    var visitor = new IsNullVisitor();
    visitor.CurrentObject = root;
    visitor.Visit(getter);
    if (visitor.IsNull)
        return null;

    return Convert.ToBoolean(visitor.CurrentObject);
} 

This method I made a nullable bool so that you can get a null value or the bool value of the object. This way, one can check whether it is null or not and if not, it will return the Boolean value of the object.

All these are very helpful if you need to return a value without the endless testing. The advantage of how I did it is that you can just use the value straight and if has a nullable object, you will still have a value. So you can use the value without testing for a nullable object first.

There are endless objects you can use and then just customize the return type value, etc. Whether you use like me a different type and as such use different names or just overloaded/override objects with the same name, it is your choice.

Points of Interest

Take note that this is unfortunately not completely idiot proof (and knowing code) on the 1st version ;-). So keep in mind that if you use the following, it will most likely crash and cause errors:

C#
string personUsername = Contracts.IsNullBoolReturn(c => c.Booking.Person.Security.Username);  

So when you use this, make sure you use the correct object. Well when in doubt, you can always just use object. :-P

C#
var personUsername = Contracts.IsNullValueReturn(c => c.Booking.Person.Security.Username); 

Lastly, be well aware that the downfall of this is that when you use any of the null checkers to strongly typed values, you must not use the value return. Although the value return is for let's say a string value, the null visitor will not return a string according to the code but an unknown variable. So the problem with this is that it does fall short when using the bool and 'N/A' returning values. But I use 90% the isNullValueReturn, very little the isNullStringReturn, and only the isNullBoolReturn and isNullNAReturn in specific reports.

History

  • Version 1

License

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


Written By
Software Developer (Senior) Pode Croject
South Africa South Africa
Life(bool alive){
if (alive) Continue;
Learn();}


WriteProgram(bool possible){
bool errorFreeCode;
if (possible || !possible) errorFreeCode = false;
else errorFreeCode = true;}


And always remember!
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
(Martin Golding)

Just a proof note for David and Carel

Comments and Discussions

 
GeneralMy vote of 5 Pin
Champion Chen3-Sep-14 5:29
Champion Chen3-Sep-14 5:29 
GeneralRe: My vote of 5 Pin
CBadger3-Sep-14 20:44
professionalCBadger3-Sep-14 20:44 
Questiontoo late Pin
giammin24-Aug-14 22:31
giammin24-Aug-14 22:31 
SuggestionAn interesting technical solution... Pin
Pete Sutcliffe21-Mar-14 1:38
Pete Sutcliffe21-Mar-14 1:38 
QuestionA better solution: the Maybe monad Pin
Sean Holm12-Mar-14 20:37
professionalSean Holm12-Mar-14 20:37 
AnswerRe: A better solution: the Maybe monad Pin
CBadger14-Mar-14 6:11
professionalCBadger14-Mar-14 6:11 
QuestionPossible improvment? Pin
Member 277125712-Mar-14 4:21
Member 277125712-Mar-14 4:21 
AnswerRe: Possible improvment? Pin
CBadger14-Mar-14 6:13
professionalCBadger14-Mar-14 6:13 
AnswerRe: Possible improvment? Pin
CBadger20-Mar-14 4:01
professionalCBadger20-Mar-14 4:01 
QuestionUsing Null-Object pattern [GoF] Pin
Member 466461211-Mar-14 3:21
Member 466461211-Mar-14 3:21 
AnswerRe: Using Null-Object pattern [GoF] Pin
CBadger11-Mar-14 9:47
professionalCBadger11-Mar-14 9:47 
GeneralRe: Using Null-Object pattern [GoF] Pin
Member 466461211-Mar-14 23:23
Member 466461211-Mar-14 23:23 
AnswerRe: Using Null-Object pattern [GoF] Pin
CBadger14-Mar-14 6:17
professionalCBadger14-Mar-14 6:17 
QuestionWhat about performance? Pin
Daniele Rota Nodari11-Mar-14 0:34
Daniele Rota Nodari11-Mar-14 0:34 
AnswerRe: What about performance? Pin
CBadger11-Mar-14 9:50
professionalCBadger11-Mar-14 9:50 
GeneralI have something similar to this from 2010 Pin
Brandon D'Imperio10-Mar-14 8:39
Brandon D'Imperio10-Mar-14 8:39 
GeneralRe: I have something similar to this from 2010 Pin
CBadger11-Mar-14 9:53
professionalCBadger11-Mar-14 9:53 
QuestionNice idea Pin
gardnerp10-Mar-14 5:50
gardnerp10-Mar-14 5:50 
AnswerRe: Nice idea Pin
CBadger11-Mar-14 9:51
professionalCBadger11-Mar-14 9:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.