ValidatableBase: Back to the drawing board

A couple of weeks ago I posted about my new ValidatableBase object I put on GitHub. It worked pretty good for Windows Universal Apps and I'm enjoying it's simplicity.

I ran in to some drawbacks when using it with WPF however. In WPF, most projects use either IDataErrorInfo or INotifyDataErrorInfo. Neither of these two interfaces worked for the business requirements I have. Neither interface make it really easy to break up your validation errors in to categories. What if want to perform validation and have it come back with a warning instead of an error? You can't do that with IDataErrorInfo and while you can do it using INotifyDataErrorInfo, it requires a decent chunk of plumbing to expose and doesn't make sense semantically. To get all errors, warnings and information messages, you access the GetErrors method or HasErrors property. It makes things confusing when building a framework and you want others to access warnings and informative messages from the model. "Just grab the warnings from the GetErrors method". Huh?

So I thought I'd just move over and ValidatableBase in WPF. The problem that I ran in to with it is that it was not easy to quickly re-evaluate a properties failed validation. I can of course re-run the entire validation process, but I wanted to have more control. Since the Func< > required by the ValidateProperty is typically an anonymous method or a private method within the model, it makes invoking the ValidateProperty method on the model really unsafe. The view model was reproduce the validation rules for the property, which it shouldn't ever do. If I exposed all of the individual validation methods within the model, then I kind of feel like I'd be breaking the Open/Closed principle.

To resolve this, I set out to re-build the ValidatableBase with a better version (still a work-in-progress). The revision will provide support for two methods. ValidateAll() and ValidateProperty(string property). These two methods wil then hit a cache of Validation Rules that each object has associated to it's properties and run the validation from those objects. As an example, I can create a StringIsNotNull validation rule object and add it via a property. When a model is instanced, it wil find all of the validation rules required and cache them. When either of the aforementioned validation methods are invoked, the applicable validation rules will get their Validate() method called and your property will be validated. If you want to re-evaluate a property, you can with ValidateProperty("Email");. This lets you abstract the actual validation rules out in to independent objects that can be re-used across your models.

The work is mostly done, but I'm having to spend some time determining the best way to provide parameters to the validation rules. Once this is ironed out, I should be able to publish a new version to GitHub.

Breaking Ground with Visual Studio Online

For my current project, I've chosen to host it on Visual Studio Online and make use of their source control, continuous integration via automated builds and Agile support.

Source Control

Visual Studio Online's source control (along with everything else) is handled through TFS. You are given the choice of using the TFS repository or a Git repository, that is managed by the TFS server. While I prefer Git, I chose the TFS repository since that is what I use at work. Since I use it on a daily basis, I am more comfortable and familiar with it. I want to spend my time learning better ways to code and building a quality product rather than spending time learning the ins and outs of Git via TFS Server.

The Visual Studio IDE has excellent source control support, so I was up and running really quick. I brought my self up to speed with the revised ALM Rangers Guide for branching strategies and got my branching system put together. Initially I'm starting with just a single branch, Main. Once the application gets to a stable point, I'll branch to a Dev branch and continue development there. I'll will ultimately end up with a Development and Release branch isolation strategy. Being a solo developer, I think this works well. It lets me stage chunks of code in Main, that will ultimately end up in Release, while continuing to develop new content in Dev until I am ready to ship Main.

Automated Builds

One of the things I'm really anxious to start doing, is automating my builds everytime I check in. Since Visual Studio online only provides you with 60 minutes of build time per month, I will probably only run continuous integration builds when I merge from my Dev branch in to Main and Release. When I am checking in code to Dev, I'm don't really need to have a build generated.

The nice part with this is that I can easily access a build of my application from anywhere. I just need to log in to Visual Studio Online, download the drop and run it. Since I don't like storing my source code on cloud services like OneDrive or Dropbox, this lets me access my source and drops from anywhere.

Agile

We use a variation of Agile at my work and I don't have any complaints with how Agile works. I used it somewhat with AllocateThis! when my buddy and I where working on some stuff together and found it helped provide a clear path for development. Typically, I just start writing code, then end up going back and re-writing code so that the new features I want work well with the old code. This time around, I'm going to plan properly.

I have crafted a series of user stories already and have a decent back log that is being built. An initial prototype of the app has already been wrote, so I've already got a decent idea of what is involved with creating a Universal Windows app. I will spend the next week or so finalizing the initial backlog, then start taking my prototype and building an actual product with it.

I'll post updates of my progress (without giving away to much detail!) as I develop the app. I kind of want to document a little bit the entire process, from start to finish, of creating a Universal Windows App Store app. Coming from WPF, it's been a bit of an adjustment. I had thought that they would be relatively the same (which they are); the differences however between the two frameworks are pretty significant and slowed my prototype development down. I imagine as I progress, my overall velocity should increase. At least until I start doing UI animation in Blend!

ValidatableBase for Universal WinRT apps on GitHub

I spent yesterday building a better way to do validations while writing a Universal WinRT app, and blogged about my end product. I decided I'd go ahead and make a GitHub project and upload the source, so it is freely available to anyone who needs Validation until prism and Universal WinRT apps can support it out of the box.

The following is from the projects ReadMe.md

ValidatableBase

Model Validation for Universal WinRT Apps. Since Universal WinRT apps targeting Windows 8.1 and Windows Phone 8.1 lack built in, easy to use data Validation, I wrote a quick model object that can be used to add validation to your apps.

An example model, providing validation making sure the name is not blank.

public class ModelFixture : ValidatableBase
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public override void Validate()
    {
        this.ValidateProperty((failureMessage) =>
            {
                if (string.IsNullOrEmpty(this.Name))
                {
                    return new ValidationErrorMessage(failureMessage);
                }
                return null;
            },
            failureMessage: "Name can not be blank!",
            propertyName: "Name");

        base.Validate();
    }
}

Advanced Example validating multiple business rules on a property, for multiple properties.

public class User : ValidatableBase
{
    /// < summary>
    /// The Email backing field.
    /// < /summary>
    private string email = string.Empty;

    /// < summary>
    /// The Password backing field.
    /// < /summary>
    private string password = string.Empty;

    /// < summary>
    /// Gets the Email.
    /// < /summary>
    /// < value>
    /// The Email.
    /// < /value>
    public string Email
    {
        get 
        {
            return this.email;
        }

        set 
        { 
            this.email = value;
            this.OnPropertyChanged("Email");
        }
    }
    /// < summary>
    /// Gets the Password.
    /// < /summary>
    /// < value>
    /// The Password.
    /// < /value>
    public string Password
    {
        get 
        { 
            return this.password; 
        }

        set 
        { 
            this.password = value;
            this.OnPropertyChanged("Password");
        }
    }

    /// < summary>
    /// Performs validation on the User.
    /// < /summary>
    public override void Validate()
    {
        this.ValidateProperty(this.ValidateEmailIsNotEmpty, "Invalid Email Address.", "Email");
        this.ValidateProperty(this.ValidateEmailIsFormatted, "Email Address is not in the correct format.", "Email");
        this.ValidateProperty(this.ValidatePasswordIsNotEmpty, "Password can not be empty.", "Password");
        this.ValidateProperty(this.ValidatePasswordIsToShort, "Password must be greater than 8 characters.", "Password");
        this.ValidateProperty(this.ValidateIfPasswordContainsSpaces, "Password must not contain spaces.", "Password");

        base.Validate();
    }

    /// < summary>
    /// Validates that the email is not empty.
    /// < /summary>
    /// < param name="failureMessage">The message to supply the error collection if validation fails.</param>
    /// < returns>Returns a ValidationErrorMessage if validation fails. Otherwise, null is returned.</returns>
    private IValidationMessage ValidateEmailIsNotEmpty(string failureMessage)
    {
        if (string.IsNullOrEmpty(this.Email))
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }

    private IValidationMessage ValidateEmailIsFormatted(string failureMessage)
    {
        string[] addressParts = this.Email.Split('@');

        if (addressParts.Length < 2)
        {
            var msg = new ValidationErrorMessage(failureMessage);
            return msg;
        }

        string[] domainPiece = addressParts.LastOrDefault().Split('.');
        if (domainPiece.Length < 2)
        {
            var msg = new ValidationErrorMessage(failureMessage);
            return msg;
        }

        return null;
    }

    /// < summary>
    /// Validates that the password is not empty.
    /// < /summary>
    /// < param name="failureMessage">The message to supply the error collection if validation fails.</param>
    /// < returns>Returns a ValidationErrorMessage if validation fails. Otherwise, null is returned.</returns>
    private IValidationMessage ValidatePasswordIsNotEmpty(string failureMessage)
    {
        if (string.IsNullOrEmpty(this.Password))
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }

    /// < summary>
    /// 
    /// < /summary>
    /// < param name="failureMessage">The message to supply the error collection if validation fails.</param>
    /// < returns>Returns a ValidationErrorMessage if validation fails. Otherwise, null is returned.</returns>
    private IValidationMessage ValidatePasswordIsToShort(string failureMessage)
    {
        if (this.Password.Length < 8)
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }

    /// < summary>
    /// Tests to see if the password contains any spaces.
    /// < /summary>
    /// < param name="failureMessage"></param>
    /// < returns></returns>
    private IValidationMessage ValidateIfPasswordContainsSpaces(string failureMessage)
    {
        if (this.Password.Contains(' '))
        {
            return new ValidationErrorMessage(failureMessage);
        }

        return null;
    }
}

View Model validation checks

This exposes validation methods to external objects as well. By inheriting from ValidatableBase, you can force validation checks on a model from within your view model.

    public bool CanExecute(object parameter)
    {
        // Perform validation on the user.
        this.AppUser.Validate();

        // Check if there are any errors.
        if (this.AppUser.HasValidationMessageType< ValidationErrorMessage>())
        {
            return false;
        }

        return true;
    }

Binding to the View

Binding to the view is really easy, using one of the two provided converters.

< Application.Resources>
    < converters:ValidationCollectionToSingleStringConverter x:Key="ValidationCollectionToSingleStringConverter" />
    < converters:IValidationMessageCollectionToStringCollectionConverter x:Key="CollectionConverter" />
< /Application.Resources>

Bind to a single error for a property.

< TextBlock x:Name="EmailValidationErrorTextBlock"
            Text="{Binding Path=AppUser.ValidationMessages[Email], 
                            Converter={StaticResource ValidationCollectionToSingleStringConverter}}"
            Foreground="Red" />

Bind to the entire collection of errors for a property

< ItemsControl ItemsSource="{Binding Path=AppUser.ValidationMessages[Password], 
                                    Converter={StaticResource CollectionConverter}}">
    < ItemsControl.ItemTemplate>
        < DataTemplate>
            < TextBlock Text="{Binding}"
                        Foreground="Red" />
        < /DataTemplate>
    < /ItemsControl.ItemTemplate>
< /ItemsControl>

Reflection in Native C#

Today, Microsoft announced that their native .NET compiler in the cloud would support reflection via XML styled directive files. Essentially, the app will hit the XML for meta-data associated with a Type it needs to reflect in order to access its Members. This is interesting but could create a headache for develoeprs in the App Store.

There is potential that this might cause problems in the apps, by having static assemblies try to do something that a dynamic assembly was designed to do. If the application crashes due to an issue with the reflection work-around Microsoft is using it will be hard for a developer to troubleshoot it. The developer will try to reproduce the steps required to crash, only to have the app work in the development environment. Since the native conversion takes place in the cloud, I would be extremely apprehensive about enabling this feature on any of my apps.

It would be one thing if I could perform this conversion locally so I can test it, but there is a lot of requirements for native compiling due to varying hardware that would make this feature expensive for local builds.

My current project doesn't make use of Reflection and so if I need to, I could make it native. My next planned project (already started on) makes heavy use of reflection so I'll probably avoid native C#.