Skip to content
Simon Mourier edited this page Feb 19, 2020 · 1 revision

This section describes all possibilities provided by SoftFluent CodeModeler in order to handle all types of rules such as validation or business rules. Recommendations will be provided in order to guide developers and architects towards a given pattern, based on the issue he must solve.

CodeModeler provides four main types of rules, starting validating properties, and up to executing business rules implemented in external libraries. The following schema is an overview of those four main types:

Rules

Apart from the transaction rule, it's possible to declare as many rules as many times as one wants.

Rules are generated in the Business Object Model (BOM) by default. This implies that they are available to all upper layers.

To add a rule to a concept from Visual Studio, you can use the contextual “Add Rule” item in the Ribbon Bar or in the surface design context menu:

Rules - Picture 186

Entity Event Rules

Entity event rules are rules which can be declared on an entity, and which allow developers to plug-in on a given event. By default, each rule generates a call to a method named after the event (e.g. the OnAddNew rule will generate a call to a OnAddNew method), however this can be modified by specifying the “Method Name” attribute of the rule. The actual method has then to be implemented by the developer.

The following entity event rules are available:

  • OnAddNew

  • OnCreate / OnBeforeCreate / OnAfterCreate

  • OnBeforeValidate / OnAfterValidate / OnBeforeValidateMember / OnAfterValidateMember

  • OnBeforeSave / OnAfterSave

  • OnBeforeDelete / OnAfterDelete

  • OnBeforeReadRecord / OnAfterReadRecord

  • OnBeforeReadRecordOnSave / OnAfterReadRecordOnSave

  • OnBeforeCopyFrom / OnAfterCopyFrom

  • OnBeforeCopyTo / OnAfterCopyTo

  • OnAddLoadParameters / OnAddSaveParameters / OnAddDeleteParameters

  • OnSerialized / OnDeserialized / OnSerializing / OnDeserializing

  • OnCollectionPageAdd

  • OnPropertyChanged

  • OnCollectionChanged

  • OnAction

Entity Event Rules - Picture 187

Notes:

  • The OnAddNew rule adds a call to a OnAddNew method that must be implemented by the developer. This call is issued in the entity collection class, in the implementation of the IBindingList.AddNew method, right before the new element is added to the list.

  • The OnCollectionPageAdd rule acts on the entity collection class and adds a call to a method when the collection is filled with records from the persistence layer.

  • The OnPropertyChanged rule adds a call to a method in the callback of the PropertyChanged event. Please note that this callback is named OnPropertyChanged, so the “Method Name” attribute must be specified.

  • The OnCollectionChanged rule acts on the entity collection class and adds a call to a method when a change on the collection happens. Please note that this call is issued in a method named OnCollectionChanged so the “Method Name” attribute must be specified.

  • The OnAction is a very generic event, called whenever an action is done on the entity (ReadRecord, Delete, Validate, etc.). It's in general better to use a more specific event, whenever possible.

The following example demonstrates how to set-up an OnBeforeCreate and OnAfterCreate event rules on the Car entity:

  • to plug-in at the BeforeCreate step, we declare an OnBeforeCreate rule and its callback in a snippet method,

  • to plug-in at the AfterCreate step, we declare an OnAfterCreate rule and its callback in a snippet method;

Entity Event Rules - Picture 192

Entity Event Rules - Picture 219

Entity Event Rules - Picture 234

This is the BOM output:

// Car constructor
public Car()
{
    this.OnBeforeCreate();
    this._entityState = CodeModeler.Runtime.EntityState.Created;
    this.OnAfterCreate();
}
 
// Snippet method
private void OnBeforeCreate()
{
    System.Diagnostics.Trace.WriteLine("OnBeforeCreate");
}
 
// Snippet method
private void OnAfterCreate()
{
    System.Diagnostics.Trace.WriteLine("OnAfterCreate");
}

Note: rule implementation doesn't necessarily have to be in a snippet. For complex actions it is recommended to code rule implementations in partial classes rather than snippets.

Property Event Rules

Available Property event rule types are:

  • OnGet

  • OnBeforeSet

  • OnAfterSet

  • OnAction

  • OnCreate

  • OnCollectionChanged

Notes:

  • The OnAction rule binds to the Action event of a binary large object.

  • The OnCollectionChanged rule binds to the ListChanged event in the getter of the given collection property.

The following example demonstrates how to declare OnBeforeSet and OnAfterSet event rules on the FirstName property of the Customer entity:

  • to plug-in at the BeforeSet step, we declare an OnBeforeSet rule and the associated OnBeforeSetFirstName snippet

  • to plug-in at the AfterSet step, we declare an OnAfterSet rule and the associated OnAfterSetFirstName snippet.

Property Event Rules - Picture 236

Property Event Rules - Picture 237

Property Event Rules - Picture 238

This is the BOM output (note the two snippet methods OnBeforeSetFirstName and OnAfterSetFirstName are called in the setter of the FirstName property):

// FirstName property
public string FirstName
{
    get
    {
        return this._firstName;
    }
    set
    {
        if ((this.OnBeforeSetFirstName(value) == false))
        {
            return;
        }
        this._firstName = value;
        this.EntityState = CodeModeler.Runtime.EntityState.Modified;
        this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("FirstName"));
        this.OnAfterSetFirstName(value);
    }
}
 
// Snippet method 'OnBeforeSetFirstName'
private bool OnBeforeSetFirstName(string value)
{
    System.Diagnostics.Trace.WriteLine("OnBeforeSetFirstName : " + value);
    return true;
}
 
// Snippet method 'OnAfterSetFirstName'
private void OnAfterSetFirstName(string value)
{
    System.Diagnostics.Trace.WriteLine("OnAfterSetFirstName : " + value);
}

Note: rule implementation doesn't necessarily have to be in a snippet. For complex actions it is recommended to code rule implementations in partial classes rather than snippets.

Method Event Rules

Available method event rule types are:

  • OnBefore

  • OnAfter

  • OnBeforeRead

  • OnAfterRead

  • OnAddParameters

The following example demonstrates how to get Before and After events on the LoadByName method of the Supplier entity:

  • to plug-in at the Before step, we declare an OnBefore rule and the associated OnBeforeLoadByName snippet

  • to plug-in at the After step, we declare an OnAfter rule and the associated OnAfterLoadByName snippet.

Method Event Rules - Picture 239

Method Event Rules - Picture 240

Method Event Rules - Picture 241

This is the BOM output (note the two snippet methods OnBeforeLoadByName and OnAfterLoadByName are called in the LoadByName method):

// LoadByName method
public static HowTo.Supplier LoadByName(string name)
{
    if ((name == ((string)(null))))
    {
        return null;
    }
    HowTo.Supplier supplier = new HowTo.Supplier();
    HowTo.Supplier.OnBeforeLoadByName(supplier, name);
    CodeModeler.Runtime.CodeModelerPersistence persistence = CodeModelerContext.Get(HowTo.Constants.HowToStoreName).Persistence;
    persistence.CreateStoredProcedureCommand("Supplier_LoadByName");
    persistence.AddParameter("@Name", name);
    System.Data.IDataReader reader = null;
    try
    {
        reader = persistence.ExecuteReader();
        if ((reader.Read() == true))
        {
            supplier.ReadRecord(reader, CodeModeler.Runtime.ReloadOptions.Default);
            supplier.EntityState = CodeModeler.Runtime.EntityState.Unchanged;
            HowTo.Supplier.OnAfterLoadByName(supplier, name);
            return supplier;
        }
    }
    finally
    {
        if ((reader != null))
        {
            reader.Dispose();
        }
        persistence.CompleteCommand();
    }
    HowTo.Supplier.OnAfterLoadByName(supplier, name);
    return null;
}
 
// Snippet method 'OnBeforeLoadByName'
private static void OnBeforeLoadByName(Supplier supplier, string name)
{
    System.Diagnostics.Trace.WriteLine("OnBeforeLoadByName : " + name);
}
 
// Snippet method 'OnAfterLoadByName'
private static void OnAfterLoadByName(Supplier supplier, string name)
{
    System.Diagnostics.Trace.WriteLine("OnAfterLoadByName : " + name);
}

Note: rule implementation doesn't necessarily have to be in a snippet. For complex actions it is recommended to code rule implementations in partial classes rather than snippets.

Transaction Rules

Transaction rules refer to rules allowing the developer or architect to declare an operation as transactional. Transaction rules can be added at three levels:

  • On a project,

  • On an entity,

  • On a method.

Transaction rules have a scope, which can be:

  • global: the rule is to be applied to all entities (e.g. a transaction rule declared at the project level),

  • local: the rule is to be applied for a particular entity or method (e.g. a transaction rule declared at the entity or method level).

If not defined explicitly, transaction rules declared at the project or the entity level concern only the Save and Delete methods. However, it's possible to wrap other methods in transactions by declaring a local transaction rule on that method.

ADO.NET transaction rules

The following example demonstrates how to declare the following transaction rules a local ADO.NET transaction rule for the Customer entity, in the Save method:

ADO.NET transaction rules - Picture 242

You can configure the ADO.NET Transaction rule if you double-click on the AdoTransaction shape tree view item.

This is the BOM output (note the transaction is handled by the CodeModelerPersistence runtime class in the Save method of the Customer class. The CodeModelerPersistence class uses ADO.NET transactions):

// Save method for the Customer class
public virtual bool Save()
{
    bool result;
    CodeModelerPersistence persistence = CodeModelerContext.Get("HowTo").Persistence;
    persistence.BeginTransaction(IsolationLevel.Snapshot);
    try
    {
        bool localSave = this.BaseSave(false);
        persistence.CommitTransaction();
        result = localSave;
    }
    catch (Exception)
    {
        persistence.RollbackTransaction();
        throw;
    }
    return result;
}

TransactionScope transaction rules

The following example demonstrates how to declare the following transaction rules a local ADO.NET transaction rule for the Customer entity, in the Save method:

TransactionScope transaction rules - Picture 244

You can configure the TransactionScope rule if you double-click on the TransactionScope shape tree view item.

Looking at the generated code, you can see that the transaction is handled by .NET’s TransactionScope class in the Save method of the Customer class:

// Save method for the Customer class
public virtual bool Save()
{
    TransactionScope scope = null;
    bool ret;
    try
    {
        TransactionOptions options = new TransactionOptions();
        scope = new TransactionScope(TransactionScopeOption.RequiresNew, options);
        options.Timeout = new TimeSpan(0x165a0bc00L);
        bool localSave = this.BaseSave(false);
        scope.Complete();
        ret = localSave;
    }
    finally
    {
        if (scope != null)
        {
            scope.Dispose();
        }
    }
    return ret;
}

Validation Rules

Validating objects is a common and important task in applications, so CodeModeler offers advanced validation capabilities based on the standard .NET validation features.

In fact, in addition to ensuring that input data is not ill-formatted, it's a common need in business applications to validate data based on a business rule; therefore, validation is a core part of the business. As exposed throughout this documentation, all the business is in the Business Object Model (BOM), consequently so are validation capabilities.

This section describes how a developer or architect can, on top of the available validation features available, add custom validation rules.

Overview

By declaring validation rules in the model (at property level), the declared validation rules will be added to the Validate method of an entity class generated by CodeModeler. By default, all CodeModeler-generated entity classes implement the IMemberValidator and the .NET standard IDataErrorInfo.

  • CodeModeler’s IMemberValidator interface allows to validate, for a given entity, either a property or all properties. The validation result is placed into an error collection which is retrievable programmatically. The Validate method on the entity generated class automatically calls this implementation.

  • IDataErrorInfo is commonly used by Windows Forms controls as well as XAML controls to provide advanced UI validation capabilities. Its CodeModeler implementation uses the IMemberValidator implementation.

On a higher level, since validation rules can be considered as a core part of the business logic, to have them gathered outside of the code, in the business model, makes them easily available and checkable to persons external from the development team such as project managers, salesman, etc.

Several validation rule types are available:

  • String validation rule in order to validate input strings,

  • Compare validation rule, in order to validate a value by comparing it to another value,

  • Regular expression validation rule, in order to validate if an input string matches the specified regular expression,

  • Object validation rule, in order to validation any object (filter null objects for instance).

  • Etc.

Moreover, it's possible to implement its own custom validator so that new validation rules can be added. More details and information are available in the following sub-sections.

Each validation rule has a set of failure codes. Failure codes are important for localization purposes, since each failure code has a corresponding message whose name is the failure code. Therefore, if you need to change the validation message for a specific validation error, you need to know the message name, which is the failure code. For a more detailed description of the logic please refer to the Messages and Resources chapter.

String Validation Rule

A String Validation Rule validates a string property.

The following example demonstrates how to declare two string validation rules for the Car entity:

  • in the validation rule for the Name property, underscore characters and null or empty string are forbidden

  • in the validation rule for the Category property, the length of the string is between 1 and 64.

String Validation Rule - Picture 246

String Validation Rule - Picture 247

This is the BOM output (note that two CodeModeler’s StringValidator instances have been added to the Car class, _nameValidator0 for Name property and _categoryValidator0 for Category property):

// String validator for Name property
private static CodeModeler.Runtime.Rules.StringValidator _nameValidator0 = new CodeModeler.Runtime.Rules.StringValidator(HowTo.Resources.ValueValidationFailureHandler.Current, -1, 2147483647, "_", ((CodeModeler.Runtime.Rules.StringValidatorOptions.NullDisallowed | CodeModeler.Runtime.Rules.StringValidatorOptions.EmptyDisallowed)
| CodeModeler.Runtime.Rules.StringValidatorOptions.Trim));
 
// String validator for Category property
private static CodeModeler.Runtime.Rules.StringValidator _categoryValidator0 = new CodeModeler.Runtime.Rules.StringValidator(HowTo.Resources.ValueValidationFailureHandler.Current, 1, 64, default(string), CodeModeler.Runtime.Rules.StringValidatorOptions.Trim)

The available attributes are:

  • Minimum Length: the minimum string length required to be a valid string.

  • Maximum Length: the maximum string length required to be a valid string.

  • Invalid Characters: a set of characters to be considered invalid.

  • Options: a combination of options. Available options are: None, NullDisallowed, EmptyDisallowed, Trim, EmptyDisallowed.

The possible failure codes are:

  • Null

  • Empty

  • MinLength

  • MaxLength

  • InvalidCharacters

Comparison Validation Rule

A Comparison Validation Rule validates a property by comparing its value with a set of reference values. Note compare validation rules are automatically typed according to the property they validate.

The following example demonstrates how to declare compare validation rules for the Manager entity:

  • City is limited to the set of non case sensitive cities {Paris, London, Berlin}

  • RelativeBonus is between 0 and 2.5 %

  • AbsoluteBonus is between 0 and 25000 dollars

  • Seniority must be greater or equal to 5 years

  • Holidays must be less or equal to 20 days.

Note values can be edited using a special culture-aware value editor.

Comparison Validation Rule - Picture 249

Comparison Validation Rule - Picture 250

Comparison Validation Rule - Picture 251

Comparison Validation Rule - Picture 252

Comparison Validation Rule - Picture 253

This is the BOM output:

static Manager()
{
    _cityValidator0 = new CompareValidator(ValueValidationFailureHandler.Current, CompareValidatorOperator.Equal, new object[] { "PARIS", "LONDON", "BERLIN" }, null, null, CompareValidatorOptions.IgnoreCase);
 
    _relativeBonusValidator0 = new CompareValidator(ValueValidationFailureHandler.Current, CompareValidatorOperator.BetweenEqual, null, 0M, 2.5M, CompareValidatorOptions.None);
 
    _absoluteBonusValidator0 = new CompareValidator(ValueValidationFailureHandler.Current, CompareValidatorOperator.BetweenEqual, null, "0", "25000", CompareValidatorOptions.None);
 
    _seniorityValidator0 = new CompareValidator(ValueValidationFailureHandler.Current, CompareValidatorOperator.GreaterThanEqual, "5", null, null, CompareValidatorOptions.None);
 
    _holidaysValidator0 = new CompareValidator(ValueValidationFailureHandler.Current, CompareValidatorOperator.LessThanEqual, "20", null, null, CompareValidatorOptions.None);
}

The available attributes are:

  • Minimum Value: the minimum accepted value (only used for binary operations).

  • Maximum Value: the maximum accepted value (only used for binary operations).

  • Value To Compare: the object value (only used for unary operations).

  • Operator: the comparison operation to perform. Available operators are: Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, DataTypeCheck, Between, BetweenEqual, StartsWith, EndsWith, Contains.

  • Options: a combination of options. Available options are: None, NullDisallowed, EmptyDisallowed, Trim, EmptyDisallowed.

  • Is Array: defines if the value is an array and must validated as such.

The possible failure codes are:

  • Failed

Regular Expression Validation Rule

A Regular Expression Validation rule validates a string property by comparing it with a regular expression.

The following example demonstrates how to declare two regular expression validation rules for the Customer entity:

  • EMail property is a valid e-mail address in the form [email protected]

  • ZipCode property is exactly a five digits string.

Regular Expression Validation Rule - Picture 254

Regular Expression Validation Rule - Picture 255

Note: There is a built-in email validator, but this is an example.

This is the BOM output:

// RegExpression validator for EMail property
private static CodeModeler.Runtime.Rules.RegularExpressionValidator _eMailValidator0 = new CodeModeler.Runtime.Rules.RegularExpressionValidator(HowTo.Resources.ValueValidationFailureHandler.Current, "[\\w]+@[\\w]+\\.[\\w]+", default(System.Text.RegularExpressions.RegexOptions), CodeModeler.Runtime.Rules.StringValidatorOptions.Trim);
 
// RegExpression validator for ZipCode property
private static CodeModeler.Runtime.Rules.RegularExpressionValidator _zipCodeValidator0 = new CodeModeler.Runtime.Rules.RegularExpressionValidator(HowTo.Resources.ValueValidationFailureHandler.Current, "[0-9]{5}", default(System.Text.RegularExpressions.RegexOptions), CodeModeler.Runtime.Rules.StringValidatorOptions.Trim);

The available attributes are:

  • Expression: the regular expression.

  • Regular Expression Options: a combination of regex options. Available values are: None, IgnoreCase, Multiline, ExplicitCapture, Compiled, Singleline, IgnorePatternWhitespace, RightToLeft, ECMAScript, CultureInvariant.

  • Options: a combination of string options. Available options are: None, NullDisallowed, EmptyDisallowed, Trim, EmptyDisallowed.

The possible failure codes are:

  • Null

  • Empty

  • RegexFailed

Object Validation Rule

The object validation rule allows developers to validate related entities. For instance, in a case where two entities are related to one another, an object validation rule could be declared on the relation properties to ensure their related entity is valid. The object validation rule must be defined on properties of Entity or EntityCollection types.

The following example demonstrates how to declare, for the Director entity, a validation rule on its Employee property. The Validate method of the Director entity will recursively call the Validate method of its Employee property.

Object Validation Rule - Picture 256

This is the BOM output:

// Director class : ObjectValidator for Employee property
private static CodeModeler.Runtime.Rules.ObjectValidator _employeeValidator0 = new CodeModeler.Runtime.Rules.ObjectValidator(HowTo.Resources.ValueValidationFailureHandler.Current, true, true);

The available attributes are:

  • Enumerate: specifies if validated objects implementing IEnumerable must be enumerated and each enumerated child validated as well.

  • Allow Null Or Empty: specifies if null objects or empty string are valid.

The possible failure codes are:

  • Null

  • Empty

Url Validation Rule

An Url Validation rule validates a string property by comparing it with an url/uri.

The following example demonstrates how to set an url rule for the Company entity:

Url Validation Rule - Picture 257

The available attributes are:

  • Kind: the url kind. Possible values are: RelativeOrAbsolute, Relative, Absolute.

  • Valid Schemes: a comma separated list of scheme strings. 'http' or 'ftp' are example of such strings.  An empty string means all schemes are valid.

  • Valid Ports: a comma separated list of port numbers. A port number is a string such as 80 (default port for http) or 443 (default port for https). An empty string means all ports are valid.

  • Valid Host Names: a comma separated list of host names. An empty string means all host names are valid.

  • Valid Host Name Types: a comma separated list of host name types. A host name type is one of the values from the System.UriHostNameType enumeration. An empty string means all host name types are valid.

  • Options: a combination of string options. Available options are: None, NullDisallowed, EmptyDisallowed, Trim, EmptyDisallowed.

The possible failure codes are:

  • Null

  • Empty

  • Failed

  • InvalidScheme

  • InvalidPort

  • InvalidHostName

  • InvalidHostNameType

Email Validation Rule

An Email Validation rule validates a string property by comparing it with an email.

The following example demonstrates how to set an email rule for the Company entity:

Email Validation Rule - Picture 258

The available attributes are:

  • Valid Domains: a comma separated list of domain names. The wildcard character '' can be used, as in '.com'.  An empty string means all domains are valid.

  • Expression: the regular expression.

  • Regular Expression Options: a combination of regex options. Available values are: None, IgnoreCase, Multiline, ExplicitCapture, Compiled, Singleline, IgnorePatternWhitespace, RightToLeft, ECMAScript, CultureInvariant.

  • Options: a combination of string options. Available options are: None, NullDisallowed, EmptyDisallowed, Trim, EmptyDisallowed.

The possible failure codes are:

  • Null

  • Empty

  • Failed

  • InvalidDomain

Luhn Code Validation Rule

A Luhn Validation rule validates a string property by checking it’s a Luhn number

A Luhn number is a number complying to the Luhn algorithm which is a checksum formula used to validate numbers. The algorithm is in the public domain and is in wide use today, mostly for identification numbers such as credit card numbers, IMEI numbers, social insurance numbers, etc. It's a simple method to distinguish valid numbers from a random collection of digits.

The following example demonstrates how to set a Luhn rule for the Customer entity:

Luhn Code Validation Rule - Picture 259

The available attributes are:

  • Allow Whitespaces: indicates whether validator allows whitespaces or not.

  • Options: a combination of string options. Available options are: None, NullDisallowed, EmptyDisallowed, Trim, EmptyDisallowed.

The possible failure codes are:

  • Null

  • Empty

  • Failed

Custom Validation Rule

A Custom Validation Rule validates a property against custom code.

The following example demonstrates how to set an url rule for the Company entity. The rule “Runtime Type Name” attribute must be set to a .NET full type name of a class that derives from CodeModeler.Runtime.Rules.ValueValidator class and overrides the required Validate method.

Custom Validation Rule - Picture 260

Custom Validation Rule - Picture 261

This is the BOM output:

// Message class : Custom validator for Culture property
private static Message.CultureValidator _cultureValidator0 = new Message.CultureValidator(HowTo.Resources.ValueValidationFailureHandler.Current);

Data Annotations Equivalence

You can configure the BOM producer to add equivalent Data Annotation validation attributes if you configure it to do so:

Data Annotations Equivalence - Picture 289

Note however that CodeModeler rules often offer more customizations capabilities that the Data Annotations ones.

Implementation Rules

Declaring an implementation rules indicates CodeModeler that the entity on which the rule is defined should implement a specific interface, usually in the Business Object Model (BOM).

The following example demonstrates how to set the following implementation rules for the Activity entity:

  • Declare that the class Activity implements the IRunnable interface,

  • Declare that the class ActivityCollection implements the IRunnableSet interface.

Implementation Rules - Picture 245

Clone this wiki locally