Signum Framework Logo
"Open framework that encourages convention over configuration, using C# code,
not XML files, to model at the right level of abstraction and achieve deadlines.
...but also has a full Linq provider, and syncs the schema for you!"
Login
RSS

Search

»



Main
Index
Map
Videos
Download
Source code
Tutorials
Forum
FAQ



Image



PoweredBy


Introduction

This page, as well as the validation system, has been deeply changed in Signum Framework 2.0.

Frequently, the type system is not rich enough to express all the restrictions you need on your data, then you have to add checking of your data at runtime. In a typical application these restrictions are written in three different places:

  • User Interface: Using custom validation controls, Javascript...
  • Business logic: Sometimes you can suddenly find some sanity checking before doing some operation.
  • Database: By using some triggers and stored procedures.
  • Your Entities: Only in the fortunate ones that already have an entities-centric application.

That makes it way harder to know when some piece of data is valid and when it is not.

Signum Framework validation strategy, based on the great Kary Shifflett CodeProject tutorial, gives us some fresh air, moving almost all the validations to the entities. The reason we can do that is:

  • We write entities first: If you just drag and drop tables, then adding validation code is harder, even introducing ad-hoc compilation tricks. It is not easy to add attributes to auto-generated fields and properties, for example.
  • Our entities are serializable: This allows us to send entities to the client and have all the validation in there as well, so the user gets quick feedback if something is wrong while introducing data.
  • We use modern UI technology: Signum Windows is only available for WPF, and we are planning Signum Web to be ASP.Net MVC only.

Our goal is to make a validations system for our entities that, on one side, implements IDataErrorInfo interface so it works nicely with WPF bindings, and on the other side if provides the best possible experience to you when writing the entities:

  • Declarative: Simple per-property validation with Attributes.
  • Imperative: Powerful per-property and global imperative validations (maybe taking other property values into account).
  • Overrideable: Even if you don't have the control of the class (lives in another assembly) you can disable it's validations and inject new ones.

Declarative (Validator attributes)

Very often (about 70% in our applications) validations consist of just checking isolated values against a certain parametrized rule:

  • This Entity can't be null
  • This number has to be between certain values
  • This string's length has to be smaller than some value
  • This string has to match some regex.
  • ...

In order to make these scenarios easy we created Validators. (ooook, It wasn't really our idea).

Validators are just attributes with some parameters that you can place in your properties (not on fields) to create simple context-free constraints on your property values. Like this:

[Serializable]
public class MyEntityDN: IdentifiableEntity
{
  [StringLengthValidator(Min=3, Max=3),StringCaseValidator(Case.Uppercase)]
  public string Name
  {
     get{...}
     set{...}
  }
}

The code above constraints Name to be an upper-case string of exactly three characters long.

Build-in Validators

There are some validators already created for you, with nice localized error messages (currently in English and Spanish only):

  [NotNullValidator]
  [StringLengthValidator(Max=100, AllowNulls=true)] //AllowNulls is default to null
  [StringCaseValidator(Case.Uppercase)] // Everything in upercase

[RegexValidator("^[0-9]{5}$", FormatName="German - Spanish Postal Code")] //Remember that validators need to create the error message [EMailValidator] // should be a valid e-Mail address [TelephoneValidator] // should be a valid telephone number (numbers, spaces and optional '+' prefix). Combine with StringLengthValidator. New SF2. [URLValidator] // should be a valid http/s URL. New SF2. [FileNameValidator] //should no contain symbols that are invalid for a Windows file name. New SF2.

[NumberIsValidator(ComparisonType.GreaterThanOrEqual, 0)] [NumberBetweenValidator(0,10)] //Not using C intervals to please user, so 10 is a valid number as well. [DecimalsValidator(3)] // Numbers should not have more than 3 decimal places, default is 2. Works only with decimal.

[CountIsValidator(ComparisonType.GreaterThanOrEqual, 3)] //Usefull for MList<T> [NoRepeatValidator] //Avoid repeated elements in a MList [DateOnlyValidator] // Really useful!! forget about this nasty seconds


ErrorMessage Image

Thought each validator has his own error massage defined in the OverrideError function, you can specify your custom one like this:

 [StringLengthValidator(Min=3, Max=3, ErrorMessage="Please write a string of just 3 characters")]
 [StringCaseValidator(Case.Uppercase, ErrorMessage="Please write the string in uppercase")]
 public string Name
 {
    get{...}
    set{...}
 }

Unfortunately, this only works if you are not planning to localize the error message. If so you can place your custom error messages using a similar technique that we used for DescriptionAttributes: Turning localization for an assembly using LocalizeDescritionsAttribute an then using {EntityName}_{PropertyName}_{ValidatorName} as your resource key.

Example for a Spanish localization.

KeyValue
MyEntityDN_Name_StringLengthValidatorPor favor, escribe una cadena usando solo 3 carácteres
MyEntityDN_Name_StringCaseValidatorPor favor, escribe una cadena en mayúsculas

Implementing your own Validator

A Validator is just a class that derives from the following class:

    [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
    public abstract class ValidatorAttribute : Attribute
    {
        public bool DisableOnCorrupt { get; set; }
        public string ErrorMessage { get; set; }

/// <summary> /// When overriden, validates the value against this validator rule /// </summary> /// <param name="value"></param> /// <returns>returns an string with the error message, using {0} if you want the property name to be inserted</returns> protected abstract string OverrideError(object value);

//Descriptive information that continues the sentence: The property should {HelpMessage} //Used for documentation purposes only public abstract string HelpMessage { get; } }

By implementing the method OverrideError as indicated you can create custom validators easily.

    public class StartsUppercaseAttribute : ValidatorAttribute
    {
        protected override string OverrideError(object obj)
        {
            string str = obj as string; 
            if(str == null || str.Length == 0)
                return null; 
            
            if(!char.IsUpper(str[0]))
                  return "The property {{0}} has the value {0}. Should start with uppercase.".Formato(str); 
  
            return null;
        }

public abstract string HelpMessage { get { return "be an uppercase string"; } } }

Now you can reuse this validator wherever you want. You can add some parametrization to your rule by using properties.

Usually it is a good idea to allow null values to pass validation, so you can reuse them more frequently and combine with NotNull if needed. StringLengthValidator, however, controls nullability for convenience.

Notice the nested curtly brackets on Format (just string.Format). You can do that to escape the first Format pass

Imperative (virtual methods)

Sometimes (about 20%) declarative validations using attributes is not enough because the validation needs some context (other properties) or is just not worth creating a validator, since the rule is not going to be reused.

In the new version, IntegrityCheck is not virtual anymore, an you are supposed to override PropertyValidation instead (protected), all the errors have to be associated with a property now.

PropertyValidation

In order to provide an imperative validation that provides a nice user experience, the UI infrastructure needs to know what field should be decorated in the UI with a red box.

The user interface has a ErrorSummary control for this kind of errors, but nothing is simpler than a red box.

By overriding PropertyValidation, we can tell the validation system witch is the wrong property. Example:

    protected override string PropertyValidation(PropertyInfo pi)
    {
        if (pi.Is(() => Name) && name == "Neo" && dateOfBirth.Year < 1999)
            return "No body was named Neo before The Matrix";

return null; }

In the new version, IDataErrorInfo.this[] is not virtual anymore, an you are supposed to override PropertyValidation instead (protected), this way you don't have to care about calling the base and concatenate.


As you see, with this method you get the PropertyInfo as a parameter, and thanks to Validator.Is extension method you can test for your entity using strongly-typed reflection. This way you don't rely on error-prone strings.

  public static bool Is<T>(this PropertyInfo pi, Expression<Func<T>> property)
  public static bool Is<S, T>(this PropertyInfo pi, Expression<Func<S, T>> property)

StateValidator

StateValidator is new from SF2.

Very often your entities can get different states defined by an Enum and, depending on the value of the state, some properties should or shouldn't be null.

You could test this imperatively using PropertyValidation method. Let's see an example using a variation of Order class from Video 1.

[Serializable]
public class OrderDN : Entity
{
    DateTime? paidOn;
    public DateTime? PaidOn
    {
        get { return paidOn; }
        set { Set(ref paidOn, value, () => PaidOn); }
    }

DateTime? shipDate; public DateTime? ShipDate { get { return shipDate; } set { Set(ref shipDate, value, () => ShipDate); } }

State state; public State State { get { return state; } set { Set(ref state, value, () => State); } }

protected override string PropertyValidation(PropertyInfo pi) { if (pi.Is(() => ShipDate)) { if (ShipDate.HasValue && State != State.Shipped) return "ShipDate has to be null if the state is {0}".Formato(State); else if (!ShipDate.HasValue && State == State.Shipped) return "ShipDate needs a value if the state is {0}".Formato(State); }

if (pi.Is(() => PaidOn)) { if (PaidOn.HasValue && State != State.Ordered) return "PaidOn has to be null if the state is {0}".Formato(State); else if (!PaidOn.HasValue && (State == State.Shipped || State == State.Paid)) return "PaidOn needs a value if the state is {0}".Formato(State); }

return null; } }

public enum State { Ordered, Paid, Shipped, Canceled }


However, as the number of states and members increase, it gets easier to introduce bugs and harder to read the code.

StateValidator allows you to define this rules using a collection initializer to emulate a table. So we could replace our implementation of PropertyValidation with this one:

    protected override string PropertyValidation(PropertyInfo pi)
    {
        return stateValidator.Validate(this, pi);
    }

static StateValidator<OrderDN, State> stateValidator = new StateValidator<OrderDN, State>( e => e.State, e => e.PaidOn, e => e.ShipDate){ {State.Ordered, false, false }, {State.Paid, true, false }, {State.Shipped, true, true }, {State.Canceled, null, null }};

As you see, in the constructor of our stateValidator we define first the State property, and then as many related properties as we need. All using Expressions.

Then, for each value in the state enum we add a bool? for each related property. Meaning:

  • True: The property is mandatory in this state (not null).
  • False: The property must be null in this state.
  • Null: The property could or could not be set in this state (rule disabled).

StateValidator is a strange animal between imperative and declarative validation. It's defined in a declarative table in a static field, and then is used in you PropertyValidation method, producing a user-friendly string error that uses the NiceToString version of your properties and states:
  • "Ship Date is necessary on state Shipped"
  • "Paid on is not allowed on state Ordered"

The class is defined like this:

public class StateValidator<E, S> : IEnumerable //Just to allow collection initializer
     where E : IdentifiableEntity
     where S : struct
{
   public StateValidator(Func<E, S> getState, params Expression<Func<E, object>>[] properties);
   public void Add(S state, params bool?[] necessary);
   public string Validate(E entity, PropertyInfo pi);
}


Override Validation Image

In order to make your business modules composable it's necessary some flexibility to change validation of entities defined in other assemblies (maybe, not even defined by you). You don't want not being able to use one module because your customer wants to make LastName optional.

Signum Framework currently provides different options to add or remove validations to your entities:

ExternalValidation

An event that each ModifiableEntity object has in order to delegate some imperative validations in another object. Useful for parent-child relationships.

public static class ModifiableEntity
{
   (...)
   [field: NonSerialized, Ignore]
   public event PropertyValidationEventHandler ExternalPropertyValidation;
   (...)
}

public delegate string PropertyValidationEventHandler(ModifiableEntity sender, PropertyInfo pi, object propertyValue);

If you subscribe to this event manually you could add Validation to your entity. For example:

MyEntityDN myEntity = (...);
myEntity.ExternalValidation += (e, pi, value)=>
{
   MyEntityDN entity = (MyEntityDN)e;
   if(pi.Is(()=> entity.Name) && (entity.Name == "AAA"))
      return "AAA is an special name and can not be used"; 
};

myEntity.Name = "A"; myEntity.PropertyCheck(()=>myEntity.Name)); /// The length of Name has to be equal to 3

myEntity.Name = "AAA"; myEntity.PropertyCheck(()=>myEntity.Name)); /// AAA is an special name and can not be used

myEntity.Name = "ABC"; myEntity.PropertyCheck(()=>myEntity.Name)); /// null (ok)

Subscribe and unsubscribe the event manually could be a little cumbersome when the entity is moved from entity to server and so. This event is usually used to add validations to child objects, one declarative way to do so is using ValidateChildPropertyAttribute over the field of the parent entity so the framework does the event wiring for you.

public class ParentEntityDN: Entity
{
    [ValidateChildPropertyAttribute]
    EntityDN entity;
    public EntityDN Entity
    {
       get{ return entity; }
       set{ Set(ref entity, value, ()=>Entity);}
    }
     
    protected virtual string ChildPropertyValidation(ModifiableEntity sender, PropertyInfo pi, object propertyValue)
    {
       if(sender == entity && pi.Is(()=>entity.Name) && ((string)propertyValue) == "AAA")
           return "AAA is an special name and can not be used"; 
    }
}

Using this technique, the events get rewired whenever you change the entity using Set method, after retrieving, and after deserializing the entity. Just like other child notifications.

StaticValidation

The previous method for injecting validations needs to track every instance to add the event handler. Since we don't use any kind of Dependency Injection at the business entity level sometimes this is not possible (the code is in other assemblies).

A much more convenient way of including imperative validations for entities that we don't have control is using PropertyPack.StaticPropertyValidation:

Validator.GetPropertyPack((MyEntityDN d) => d.Name).StaticPropertyValidation += (e, pi, value)=>
{
   MyEntityDN entity = (MyEntityDN)e;
   if(entity.Name == "AAA") //or (string)value == "AAA"
      return "AAA is an special name and can not be used"; 
};


Notice how we din't have to code pi.Is(()=> entity.Name) this time, becuase PropertyPack allready works at the property level.

In order to make it work we need to register this change in the validation in every single start project that uses your entity (Client, Server, Loading Application...). Consider create a method or static constructor in a shared assembly.

Validators Collection

Another way of extending validations for all the entities of a given class is modifying PropertyPack.Validators collection. This List<ValidationAttributes> contains the cache of all the ValidationAttributes found in every entity and property. You can just add or remove ValidationAttributes to this entity and it will affect the validation of all the entities of this type.

Let's see how to remove the Uppercase constraint and add a new Lowercase one.

var list = Validator.GetPropertyPack((MyEntityDN d) => d.Name).Validators;
list.RemoveAll(va=>va is StringCaseValidatorAttribute);
list.Add(new StringCaseValidatorAttribute(Case.Lowercase)); 


Quite flexible, huh?

Skip some validations

Finally, you can turn off imperative internal validations (PropertyValidation) and imperative external validations (ExternalPropertyValidation), or just not validate a property at all using PropertyPack flags:

Validator.GetPropertyPack((MyEntityDN d) => d.Name).DoNotValidate = true; //Turns off validation for the property
Validator.GetPropertyPack((MyEntityDN d) => d.Name).SkipPropertyValidation = true; //Turns off PropertyValidation execution for the property
Validator.GetPropertyPack((MyEntityDN d) => d.Name).SkipExternalPropertyValidation = true; //Turns off firing ExternalPropertyValidation event for the property


Validation Strategy

ModifiableEntity is the base class with Validation support. (See more about this class in Change Tracking and Base Entities).

ModifiableEntity implements explicitly IDataErrorInfo interface:

// Provides the functionality to offer custom error information that a user interface can bind to.
public interface IDataErrorInfo
{
    // Gets an error message indicating what is wrong with this object.
    string Error { get; }

// Gets the error message for the property with the given name. string this[string columnName] { get; } }

So this is what we have to provide for a good WPF experience. AS we have seen in some previous ecamples, Signum Framework however we don't use this methods for validating the entity. Instead:

public class ModifiableEntity
{ 
   public string IntegrityCheck(); //Full entity integrity check by checking all the properties. 
 
   public string PropertyCheck<T, S>(Expression<Func<T, S>> property)  //weakly-typed overload
         where T : ModifiableEntity
   public string PropertyCheck(string propertyName)  //weakly-typed overload 
   public string PropertyCheck(PropertyPack pp) //Base overload for performance
}

  • IDataErrorInfo.Error: Calls IntegrityCheck.
  • IntegrityCheck: Call PropertyCheck for all the properties and concatenates the errors.
  • IDataErrorInfo.this[]: Calls PropertyCheck.
  • PropertyCheck: if DoNotValidate is not set, gets the first error for the property in this order:
    1. ValidatorAttributes: Get the first error of the PropertyPacks registered in Validator.
    2. PropertyValidation: Imperative internal method, if SkipPropertyValidation is not true
    3. ExternalPropertyValidation: Imperative external event, if SkipPropertyValidation is not true
    4. StaticPropertyValidation: Imperative static event, if not null.

FullIntegirtyCheck & IdentifiableIntegrityCheck

IntegrityCheck method should only take care of the entity itself. Other child entities could be wrong as well but parents shouldn't revalidate that for the sake of avoiding duplication.

Instead you can use Modifiable's FullIntegrityCheck method that walks along the full object graph and returns a big paragraph of all the entities wrong, with its path from the root and the actual errors they have.

Also, you can use IdentifiableEntity's IdentifiableIntegrityCheck, that does the same thing but for a smaller graph, the identifiable graph.

See more about Entity Graphs.

Notify Changes

As we have seen, using PropertyValidation you can make a property validation dependant of another property like this:

    public class PersonDN : Entity
    {
        string name;
        public string Name
        {
            get { return name; }
            set { Set(ref name, value, ()=>Name); }
        }        

DateTime dateOfBirth; public DateTime DateOfBirth { get { return dateOfBirth; } set { Set(ref dateOfBirth, value, ()=>DateOfBirth); } }

public override string PropertyValidation(PropertyInfo pi) { if(pi.Is(()=>Name) && Name == "Neo" && DateOfBirth.Year < 1999) return "No body was named Neo before The Matrix"; } }

The previous code is nice, but there's a minor bug if you want a top quality user experience in WPF. If you change dateOfBirth, how does Name know that it needs to re-evaluate validation? It just doesn't if you don't tell it. Just call Notify after setting dateOfBirth field like so:

    public DateTime DateOfBirth
    {
       get { return dateOfBirth; }
       set 
       { 
          if(Set(ref dateOfBirth, value, ()=>DateOfBirth))
             Notify(()=>Name); 
       }
    }

Finally, you could use ChildCollectionChanged and ChildItemPropertyChanged methods explained in Change Tracking to force revalidate some properties when your children change.

Corrupt Entities (Advanced Topic)

Finally, some entities could have optional Corruptness support by inheriting from CorruptEntity.
    [Serializable] // Just a pattern
    public class CorruptEntity : Entity
    {
        bool corrupt;
        public bool Corrupt
        {
            get { return corrupt; }
            set { Set(ref corrupt, value, () => Corrupt); }
        }

protected internal override void PreSaving(ref bool graphModified) { base.PreSaving(ref graphModified);

if (Corrupt) { string integrity = base.IdentifiableIntegrityCheck(); // So, no corruption allowed if (string.IsNullOrEmpty(integrity)) this.Corrupt = false; } }

public override string IdentifiableIntegrityCheck() { using (Corrupt ? Corruption.Allow() : null) { return base.IdentifiableIntegrityCheck(); } } }

Note that corruptess is now a pattern not an interface, so if you don't want to inherit from CorruptEntity just integrate CorruptEntity's code in your entity.

Corruptness allows some entities to have more flexible validations (the ones you allow) while corrupt. This is useful since it gives a unified model for all the dirty tricks you will had to do in order to load your customer legacy data.

So what you do is save an entities with Corrupt=true.

  • If it has no errors at all it will be saved and corruptness disappears.
  • If it has allowed errors only, it will be saved but remain corrupt
  • If it has errors that aren't allowed it won't be saved, as usual.

From the UI point of view, however, they all look like normal problems. You can even find entities by corruptness status. That means that you can let your customers fix their own data without removing your validations for the new 'pure an clean' entities.

Here you have an example of a full corruptness-enabled entity

    public class PersonDN : Entity, ICorrupt
    {
        string name;
        [StringLengthValidator(AllowNulls = false, DisableOnCorrupt=true)]
        public string Name
        {
            get { return name; }
            set { Set(ref name, value, ()=>Name); }
        }

DateTime dateOfBirth; public DateTime DateOfBirth { get { return dateOfBirth; } set { Set(ref dateOfBirth, value, ()=>DateOfBirth); } } public override string PropertyValidation(PropertyInfo pi) { if(pi.Is(()=>Name) && !Corruption.Allowed && Name == "Neo" && DateOfBirth.Year < 1999) return "No body was named Neo before The Matrix"; } }

Note the DisableOnCorrupt property on Name's validator. DisableOnCorrupt is defined in Validator class, so you can use it on any Validator

Important Note: In the condition if(!Corruption.Allowed) we don't use the Corrupt property because we want it to be marked as wrong on the user interface, but not on the server. We manage to do this because Corruption.Allowed is true in the client context and false in the server one.
Creative Commons License Signum Framework Site by Signum Software is licensed under a Creative Commons Attribution 3.0 License.
Powered by ScrewTurn Wiki version 3.0.5.600.