DSL Tools #4 - Add and Change Rules
Extending the previous question about dynamical default values, there is a scenario on my domain model that requires a different approach to achieve the same kind of behavior.
The domain model includes an embedding relation between 2 domain properties:
This relation is presented to the user in the resulting model as a compartment, where Contract is a compartment shape and ContractProperty is a compartment named properties:
I wanted to achieve two special behaviors:
- When adding a new property, if the user doesn't specify its name, then it should default to "Property".
- When modifying the name of an existing property, if the user specifies a new empty name, it should also default to "Property".
Both behaviors can be achieved using two interesting constructs of the DSL Tools: Add Rules and Change Rules.
1. Create a new AddRule for the Property class
You need to create a class that derives from AddRule and specify the RuleOn attribute that defines to which domain class the rule should be applied.
This class should override the ElementAdded method that is always called when a new element of that domain class is created:
using System;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
namespace MyModel.Processes
{
/// <summary>
/// Implements one add rule for the Property domain class.
/// </summary>
[RuleOn(typeof(Property), FireTime = TimeToFire.TopLevelCommit)]
public class PropertyAddRule : AddRule
{
#region Public Methods
public override void ElementAdded(ElementAddedEventArgs e)
{
// Validations
if (e == null)
{
throw new ArgumentNullException("e");
}
// Ignore change
if (GeneralHelper.IgnoreChange(e.ModelElement)) return;
// Ensure the name is defined
Property property = e.ModelElement as Property;
if (property != null)
{
if (string.IsNullOrEmpty(property.Name))
{
property.Name = Properties.Resources.Property;
}
}
}
#endregion
}
}
2. Create a new ChangeRule for the Property Class
This is basically the same of the previous, except that this class should derive from ChangeRule:
using System;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
namespace MyModel.Processes
{
/// <summary>
/// Provides change rules for the Property domain class.
/// </summary>
[RuleOn(typeof(Property), FireTime = TimeToFire.TopLevelCommit)]
public class PropertyChangeRule : ChangeRule
{
#region Public Methods
/// <summary>
/// Raised when a property of the model element is changed.
/// </summary>
/// <param name="e">Event arguments.</param>
public override void ElementPropertyChanged(
ElementPropertyChangedEventArgs e)
{
// Validations
if (e == null)
{
throw new ArgumentNullException("e");
}
// What property is being changed?
if (e.DomainProperty.Id.Equals
(Property.NameDomainPropertyId))
{
HandleNamePropertyChanged(e.ModelElement);
}
}
#endregion
#region Private Methods
/// <summary>
/// Invoked when the Name property of the model element
/// is changed.
/// </summary>
/// <param name="modelElement">Model element being
/// changed.</param>
private void HandleNamePropertyChanged
(ModelElement modelElement)
{
// Ignore change?
if (GeneralHelper.IgnoreChange(modelElement)) return;
// Add a default value
Property property = (Property)modelElement;
if (string.IsNullOrEmpty(property.Name))
{
property.Name = Properties.Resources.Property;
}
}
#endregion
}
}
3. Add the 2 new custom types to the domain model definition
In order for these new custom classes to be recognized by the DSL Tools runtime environment you will need to customize another class.
So, I created a new partial class named BusinessProcessesDesignerDomainModel and added the implementation of GetCustomDomainModelTypes:
using System;
namespace MyModel.Processes
{
/// <summary>
/// Custom BusinessProcessesDesignerDomainModel methods.
/// </summary>
public partial class BusinessProcessesDesignerDomainModel
{
/// <summary>
/// Returns the non-generated domain model types.
/// </summary>
/// <returns>An array of types.</returns>
protected override Type[] GetCustomDomainModelTypes()
{
// Return the rules that should be evaluated for
// the model
return new System.Type[] { typeof(PropertyAddRule),
typeof(PropertyChangeRule) };
}
}
}
Notice that this class is defined in DomainModel.cs and will have a different name depending on the name of your domain model (property Name of the root DSL definition). Something like <YourDomainModelName>DomainModel.