Validation
I have a model like so
Product 1----* ProductVersion
ProductVersion 1----* ProductEdition
ProductVersion can been in one of two states: UnderDevelopment / Released
ProductEdition has a DownloadUrl:string attribute which is only required if self.version.status = #Released
The validation for ProductEdition works perfectly, I cannot leave the DownloadUrl blank if the ProductVersion has already been released. Unfortunately when I already have a number of ProductEdition
instances with no DownloadUrl and then make my Productversion live the editions are not validated because they are not dirty. So I needed some way to ensure that when ProductVersion is validated all related
ProductEdition instances are also validated.
Step 01: Add a way to allow ProductVersion to identify other objects to be validated.
In the business classes project I added the following interface.
My ProductVersion can do this
Step 02: Create a validation service which validates all objects : Only implemented methods are shown
Step 03: Register the service in the EcoSpace
Now I can validate a list of dirty objects using EcoSpace.GetEcoService().GetConstraintsForObjects.
Step 04: Last point of defence, ensure that no invalid objects may be saved. Only relevant methods are shown.
Step 05: Replace the standard IPersistenceService in the EcoSpace
Finally I have an implementation which does the following
A: Allows me to get constraints for dirty objects + all relevant objects
B: Prevents the app from saving objects with broken constraints.
Product 1----* ProductVersion
ProductVersion 1----* ProductEdition
ProductVersion can been in one of two states: UnderDevelopment / Released
ProductEdition has a DownloadUrl:string attribute which is only required if self.version.status = #Released
The validation for ProductEdition works perfectly, I cannot leave the DownloadUrl blank if the ProductVersion has already been released. Unfortunately when I already have a number of ProductEdition
instances with no DownloadUrl and then make my Productversion live the editions are not validated because they are not dirty. So I needed some way to ensure that when ProductVersion is validated all related
ProductEdition instances are also validated.
Step 01: Add a way to allow ProductVersion to identify other objects to be validated.
In the business classes project I added the following interface.
public interface IValidationExtender
{
IEnumerable GetConstraintedObjects();
}
My ProductVersion can do this
IEnumerable IValidationExtender.GetConstraintedObjects()
{
List result = new List();
foreach (IObject currentEdition in Editions)
result.Add(currentEdition.AsIObject());
return result;
}
Step 02: Create a validation service which validates all objects : Only implemented methods are shown
public class ExtendedConstraintProvider : IConstraintProvider
{
private IConstraintProvider ModeledConstraintProvider;
public void GetConstraintsForObject(IObject instance, List constraints)
{
if (instance == null)
throw new ArgumentNullException("instance");
//Deletegate to GetConstraintsForObjects
GetConstraintsForObjects((IObjectList)instance.GetAsCollection(), constraints);
}
public void GetConstraintsForObjects(IObjectList objectList, List constraints)
{
if (objectList == null)
throw new ArgumentNullException("objectList");
if (objectList.Count == 0)
return;
//Get all constrained objects
Dictionary includedObjects = new Dictionary();
foreach (IObject currentObject in objectList)
RecursiveGetExtendedObjects(currentObject, includedObjects);
//Add the objects to a list
IObjectList newInstances = EcoServiceHelper.GetVariableFactoryService(objectList[0]).CreateUntypedObjectList(true);
foreach (KeyValuePair kvp in includedObjects)
newInstances.Add(kvp.Key);
//Return the constraints from ModeledConstraintProvider
ModeledConstraintProvider.GetConstraintsForObjects(newInstances, constraints);
}
private void RecursiveGetExtendedObjects(IObject currentObject, Dictionary includedObjects)
{
//Don't process the same object twice
if (includedObjects.ContainsKey(currentObject))
return;
includedObjects.Add(currentObject, null);
//If the class implements IValidationExtender then add its constrained objects
IValidationExtender extender = currentObject.AsObject as IValidationExtender;
if (extender != null)
{
foreach (IObject dependentObject in extender.GetConstraintedObjects())
RecursiveGetExtendedObjects(dependentObject, includedObjects);
}
}
}
Step 03: Register the service in the EcoSpace
public InteevoWebsiteEcoSpace(): base()
{
InitializeComponent();
RegisterEcoService(typeof(IConstraintProvider), new ExtendedConstraintProvider());
}
Now I can validate a list of dirty objects using EcoSpace.GetEcoService
Step 04: Last point of defence, ensure that no invalid objects may be saved. Only relevant methods are shown.
internal class ValidatingPersistenceService : IPersistenceService
{
private IPersistenceService Inner;
private IEcoServiceProvider ServiceProvider;
internal ValidatingPersistenceService(IEcoServiceProvider serviceProvider)
{
if (serviceProvider == null)
throw new ArgumentNullException("serviceProvider");
ServiceProvider = serviceProvider;
Inner = ServiceProvider.GetEcoService();
if (Inner == null)
throw new ArgumentException("ServiceProvider did not provide an instance for IPersistenceService");
}
private IConstraintProvider constraintProvider;
private IConstraintProvider ConstraintProvider
{
get
{
if (constraintProvider == null)
{
constraintProvider = ServiceProvider.GetEcoService();
if (constraintProvider == null)
throw new InvalidOperationException("IConstraintProvider not registered as an ECO service");
}
return constraintProvider;
}
}
void IPersistenceService.UpdateDatabaseWithList(IObjectList list)
{
ValidateObjects(list);
Inner.UpdateDatabaseWithList(list);
}
private void ValidateObjects(IObjectList objects)
{
List constraints = new List();
ConstraintProvider.GetConstraintsForObjects(objects, constraints);
foreach (DroopyEyes.EcoExtensions.Validation.IConstraint currentConstraint in constraints)
{
if (!currentConstraint.IsValid)
{
throw new InvalidOperationException(
string.Format("Cannot update database with invalid objects:\r\n{0} : {1}",
currentConstraint.Instance.UmlClass.Name, currentConstraint.Name)
);
}
}//foreach constraint
}
}
Step 05: Replace the standard IPersistenceService in the EcoSpace
public InteevoWebsiteEcoSpace(): base()
{
InitializeComponent();
RegisterEcoService(typeof(IPersistenceService), new ValidatingPersistenceService(this));
RegisterEcoService(typeof(IConstraintProvider), new ExtendedConstraintProvider());
}
Finally I have an implementation which does the following
A: Allows me to get constraints for dirty objects + all relevant objects
B: Prevents the app from saving objects with broken constraints.
Comments