Archive | February 2014

C# Probably Getting New “Safe Navigation” Operator “?.”

It looks as if the Visual Studio dev team may be implementing a new operator in a future .NET release. This is due in large part to community demand, which is pretty cool because it shows that the VS team is listening to their customer base; a key part of a successful product.

This new operator is likely going to take the syntax of ?. and is known as the Safe Navigation Operator.

Its purpose is rather simple, and it eliminates a load of boiler-plate code by making the compiler do it for you.

Say you have these classes:

public class A
{
    public B B { get; set; }
}

public class B
{
    public C C { get; set; }
}

public class C
{
    public int D { get; set; }
}

It’s plain to see above that it’s possible that an instance of A may have a null B property, or even that an instance of B may have a null C property. If you want to get D from A safely (so as to avoid null reference exceptions), your code often ends up looking something like this:

int? result = A.B == null ? (int?)null : 
    (A.B.C == null ? (int?)null : A.B.C.D);

What an incredibly annoying pain to write out, and even to read. You could maybe make it clearer, at the cost of more verbosity and lines of code:

int? result = null;
if (A.B != null)
{
    if (A.B.C != null)
    {
        result = A.B.C.D;
    }
}

This is still a bit convoluted and boiler-plate for my liking. Really, all we want to do in English is get the integer value of D if it exists on A. It shouldn’t be so challenging!

Enter the new Safe Navigation Operator and how it might function:

int? result = A?.B?.C.D;

Note the ?. that is placed at the point where the A instance references B and B instance references C. This is much more readable, and intuitive to look at. To me, it says “if A’s B is not null and B’s C is not null, return D, else return null” which is pretty streamlined. Upon simple inspection, it also clearly says that A.B is nullable and B.C is nullable, which are great side effects of the shortened syntax.

Note that the final product, if included in C#, may behave differently than I’ve demonstrated and hypothesized here. The code in this post is mostly made on assumptions of compiler rules for this operator, and could in the future prove to be wrong. However, even still, this operator will be a great addition to C# and .NET if it makes the cut!

Trigger IValidatableObject.Validate When ModelState.IsValid is false

I recently came across an ASP.NET MVC issue at work where the validation for my Model was not firing correctly. The Model implemented the IValidatableObject interface and thus the Validate method which did some specific logic to ensure the state of the Model (the ModelState). This Model also had some DataAnnotation attributes on it to validate basic input.

Long story short, the issue I encountered was that when ModelState.IsValid == false due to failure of the DataAnnotation validation, the IValidatableObject.Validate method is not fired, even though I needed it to be. This problem arose due to a rare situation in which ModeState.IsValid was initially false but was later set to true in the Controller’s Action Method by some logic that removed errors from the ModelState.

I did some research and learned that the DefaultModelBinder of ASP.NET MVC short-circuits it’s logic: if the ModelState is not valid (AKA is false), the IValidatableObject logic that runs the Validate method is never fired.

To thwart this, I created a custom Model Binder, a custom Model Binder Provider (to serve my custom Model Binder), and then registered the Model Binder Provider in the Application_Start method of Global.asax.cs. Here’s the code for a custom Model Binder that always fires the IValidatableObject.Validate method, even if ModelState.IsValid == false:

ForceValidationModelBinder:

/// <summary>
/// A custom model binder to force an IValidatableObject to execute the Validate method, even when the ModelState is not valid.
/// </summary>
public class ForceValidationModelBinder : DefaultModelBinder
{
    protected override void OnModelUpdated( ControllerContext controllerContext, ModelBindingContext bindingContext )
    {
        base.OnModelUpdated( controllerContext, bindingContext );

        ForceModelValidation( bindingContext );
    }

    private static void ForceModelValidation( ModelBindingContext bindingContext )
    {
        // Only run this code for an IValidatableObject model
        IValidatableObject model = bindingContext.Model as IValidatableObject;
        if( model == null )
        {
            // Nothing to do
            return;
        }

        // Get the model state
        ModelStateDictionary modelState = bindingContext.ModelState;

        // Get the errors
        IEnumerable<ValidationResult> errors = model.Validate( new ValidationContext( model, null, null ) );

        // Define the keys and values of the model state
        List<string> modelStateKeys = modelState.Keys.ToList();
        List<ModelState> modelStateValues = modelState.Values.ToList();

        foreach( ValidationResult error in errors )
        {
            // Account for errors that are not specific to a member name
            List<string> errorMemberNames = error.MemberNames.ToList();
            if( errorMemberNames.Count == 0 )
            {
                // Add empty string for errors that are not specific to a member name
                errorMemberNames.Add( string.Empty );
            }

            foreach( string memberName in errorMemberNames )
            {
                // Only add errors that haven't already been added.
                // (This can happen if the model's Validate(...) method is called more than once, which will happen when there are no property-level validation failures)
                int index = modelStateKeys.IndexOf( memberName );

                // Try and find an already existing error in the model state
                if( index == -1 || !modelStateValues[index].Errors.Any( i => i.ErrorMessage == error.ErrorMessage ) )
                {
                    // Add error
                    modelState.AddModelError( memberName, error.ErrorMessage );
                }
            }
        }
    }
}

ForceValidationModelBinderProvider:

/// <summary>
/// A custom model binder provider to provide a binder that forces an IValidatableObject to execute the Validate method, even when the ModelState is not valid.
/// </summary>
public class ForceValidationModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder( Type modelType )
    {
        return new ForceValidationModelBinder();
    }
}

Global.asax.cs:

protected void Application_Start()
{
    // Register the force validation model binder provider
    ModelBinderProviders.BinderProviders.Clear();
    ModelBinderProviders.BinderProviders.Add( new ForceValidationModelBinderProvider() );
}