LightSwitch and NonSQL Data Structures with Server Side Validation

When working with LightSwitch (HTML Client or not) you define your entity sets via the Designer and set their relations. As the whole database schema is automatically created and maintained for you by the LightSwitch runtime you have relatively little control over how to handle changes once you have to alter your entity sets. In this post I show a solution on how you can use a single column in your LightSwitch entity set to represent multiple properties of objects of any kind. Though these objects appear only as a data string to the LightSwitch object model you can still validate them on the server in a typeful way.

The main idea is based on the “Being serious about the command table pattern” approach (you can also check a previous post on how to add security to it) and implemented by defining a “data blob” in one of your LightSwitch columns and use that for storing arbitrary data structures. Data in this “blob” will be stored as a JSON string so it remains easily accessible by Javascript clients and eliminates the need of conversion when transferred over HTTP. To still allow for server side validation such objects will be defined as regular C# classes with public properties representing the actual keys of the JSON dictionary (blob). Validation is then implemented via standard System.ComponentModel.DataAnnotations attributes, so the only thing left to do is to override the “Validate” method for the entity being validated as described in Manual Validation with Data Annotations and elsewhere.

So here is an example on how this could look like. We would like to define a “Command” that updates the repositories of our clusters based on a “Region”, “Site” and optionally a “Cluster”.

  1. We define a “Command” table that holds the following attributes:
    “Id” – standard PK as defined by LightSwitch
    “Name” – the mandatory name of our “Command” to specify which Parameters class we have to use, in our example we use “UpdateRepositoryParameters”
    “Description” – some optional field that the user can use to specify a description or a reason for executing this command
    “Parameters” – our mandatory string “blob” that holds the “UpdateRepositoryParameters” class as a JSON encoded string
LS-JSON-TypeValidation1

LS-JSON-TypeValidation1

  1. We then define our “UpdateRepositoryParameters” class with the following attributes:
    “Name” – the required name of the repository
    “Region” – the mandatory string specifying the region for that repository
    “Site” – the mandatory string specifying the site for that repository
    “Cluster” – the optional string specifying the cluster for that repository (all clusters in that Site will be updated if not specified)

Note: the DataAnnotations only work with Properties and NOT with fields!

// UpdateRepositoryParameters.cs
// Make sure you set the Attributes on the public attribute
// Of course you do not have to use private fields 
// with public encapsulation and getters/setters
using System.ComponentModel.DataAnnotations;

namespace LightSwitchApplication
{
  class UpdateRepositoryParameters
  {
    private string _Name;
    [Required]
    [MinLength(8)]
    [MaxLength(16)]
    public string Name
    {
      get { return _Name; }
      set { _Name = value; }
    }
    private string _Region;
    [Required]
    [MinLength(4)]
    [MaxLength(4)]
    public string Region
    {
      get { return _Region; }
      set { _Region = value; }
    }
    private string _Site;
    [Required]
    [MinLength(3)]
    [MaxLength(3)]
    public string Site
    {
      get { return _Site; }
      set { _Site = value; }
    }
    private string _Cluster;
    [MaxLength(32)]
    public string Cluster
    {
      get { return _Cluster; }
      set { _Cluster = value; }
    }
  }
}
  1. For the actual validation we override the built-in “Validate” method for the “Command” entity set. We use the static “System.ComponentModel.DataAnnotations.Validator” class and append the error message from the validation framework to LightSwitch’s “EntitySetValidatorResultsBuilder”:
// _ApplicationDataService.lsml.cs
using Microsoft.LightSwitch;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Script.Serialization;

partial void Commands_Validate(Command entity, EntitySetValidationResultsBuilder results)
{
  var fn = string.Format("{0}:{1}{2}", this.GetType().Namespace, this.GetType().Name, System.Reflection.MethodBase.GetCurrentMethod().Name);
  System.Diagnostics.Debug.WriteLine(fn);
  try
  {
    var jss = new JavaScriptSerializer();
    var parameters = jss.Deserialize<UpdateRepositoryParameters>(entity.Parameters);

    var validationContext = new ValidationContext(parameters);
    var validationResults = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
    var fValid = Validator.TryValidateObject(parameters, validationContext, validationResults, true);
    if (!fValid)
    {
      foreach (var validationResult in validationResults)
      {
        System.Diagnostics.Debug.WriteLine(validationResult.ErrorMessage);
        results.AddEntityError(validationResult.ErrorMessage);
      }
    }
  }
  catch(Exception ex)
  {
    throw new Exception(string.Format("{0}: Exception occurred.", fn), ex);
  }
}
  1. We then create an “Add” screen to create a new “UpdateRepository” Command and define some custom properties that will hold our several “UpdateRepositoryParameters” properties:
LS-JSON-TypeValidation2

LS-JSON-TypeValidation2

  1. As the “Parameters” property is required and will be set before saving, we initialize it with a blank string. As well we set the actual Command name. We could also pre-initialize the fields (in our example it is commented out):
// UpdateRepository.lsml.js

/// <reference path="~/GeneratedArtifacts/viewModel.js" />

myapp.UpdateRepository.created = function (screen) {
  screen.Command.Parameters = " ";
  screen.Command.Name = "UpdateRepository";
  //screen.details.properties.ParametersName.value = "NewRepository";
  //screen.details.properties.ParametersRegion.value = "EMEA";
  //screen.details.properties.ParametersSite.value = "BRN";
};
myapp.UpdateRepository.beforeApplyChanges = function (screen) {
  var _params = {};
  _params["Name"] = screen.details.properties.ParametersName.value;
  _params["Region"] = screen.details.properties.ParametersRegion.value;
  _params["Site"] = screen.details.properties.ParametersSite.value;
  _params["Cluster"] = screen.details.properties.ParametersCluster.value;
  screen.Command.Parameters = JSON.stringify(_params);
};

Now that we try to add a Command with a “Cluster” name that is too long we end up with an entity error and a message that is auto-generated from the DataAnnotations framework:

LS-JSON-TypeValidation3

LS-JSON-TypeValidation3

When all parameters are valid the validator returns true and the entity can be saved successfully:

LS-JSON-TypeValidation4

LS-JSON-TypeValidation4

LS-JSON-TypeValidation5

LS-JSON-TypeValidation5

So with little work we can leverage the “DataAnnotations” framework and use it in our LightSwitch application (or anywhere else) and even apply this to data types that are only available a JSON encoded strings in the database (and the client). At the same time we can store data in a NonSQL way with LightSwitch freeing us from defining too many relationships and keeping track of database schema changes (where it makes sense).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: