Rolling up child broken rules in Silverlight

Rolling up child broken rules in Silverlight

Old forum URL: forums.lhotka.net/forums/t/6661.aspx


Charleh posted on Tuesday, March 24, 2009

Two questions in one here,

I'm not sure if I'm doing this the best way, but on a business form I'm desgining I have a  root object and multiple child objects bound.

I want the child objects to be able to be flagged or unflagged as 'main', but I want one and only one to be flagged.

If the user flags multiple children the object should be invalidated.

At the moment I've got it working by adding a new rule at the root object level which checks all the children to see how many are flagged. To make this work, I've added a new boolean property which the rule is attached to. I've overridden OnChildChanged event and in the handler I've toggled the new property from true/false to fire the validation rule and check the children.

It's a bit of a hack, is there a better way to re-validate the roots business rules on change of child objects?

Also is there a way to roll up all the broken rules on a root object and display them in one error provider on the page? The problem is, with multiple pages used, the user can not see all the broken rules, so they have no idea why the save button is disabled. I want to be able to add an error provider next to the save button which will display all errors on the root object.

JoeFallon1 replied on Tuesday, March 24, 2009

Both of your questions have been discussed many times here. The answers are long and invovled. Try searching first and then come back with refined questions.

The basic answer is that yes there are ways to do both.

Joe

Charleh replied on Wednesday, March 25, 2009

I found some code on the forums which worked for the full version of CSLA, it didn't work for Light as IBindableList in CSLALight doesn't implement enumerableness. I modded IBindableList and I've rewritten the procedure in c# to get a List<string> of the errors which I then bind to a custom error control to display them.

Anyway, it's working now so no more confused users!

Thanks!

RockfordLhotka replied on Wednesday, March 25, 2009

Charleh:

as IBindableList in CSLALight doesn't implement enumerableness

What does this mean exactly? Perhaps this is a bug or oversight that should be corrected?

Charleh replied on Wednesday, March 25, 2009

Well, the IBindingList implementation in CSLALight doesn't seem to allow iteration.

The code I found on the forum looped through the objects on a business object by casting it to IBindingList and iterating through it. It wouldn't work like that because IBindingList isn't enumerable.

I added IList to the definition of IBindingList in the CslaLight project and that fixed the problems.

public interface IBindingList : IList

My code now is as follows:

public static class ErrorRollup

{

// <summary>

// Get text for errors if object is invalid - should work with any CSLA business object

// </summary>

// <returns>Error text for invalid object</returns>

// <remarks></remarks>

public static List<string> GetErrorInformation(Object target)

{

List<String> inspectedObjects = new List<String>();

return GetErrorInformation(target, inspectedObjects);

}

// <summary>

// Get text for errors for one object based on IDataErrorInfo interface

// </summary>

// <param name="target">Object to get error text for</param>

// <param name="inspectedObjects">List of hash codes of objects that are inspected.

// This is necessary to avoid infinite recursion.

// </param>

// <returns>Text for errors for one object based on IDataErrorInfo interface</returns>

// <remarks></remarks>

private static List<string> GetErrorInformation(Object target, List<String> inspectedObjects)

{

List<string> returnValue = new List<string>();

string targetID = string.Empty;

if (target == null)

{

return returnValue;

}

targetID = target.GetType().ToString() + ":" + target.GetHashCode().ToString();

if (!inspectedObjects.Contains(targetID))

{

inspectedObjects.Add(targetID);

if (target is IBindingList)

{

// check error messages for each row in the list

for(int oneItem = 0; oneItem < ((IBindingList)target).Count; oneItem++)

{

if (((IBindingList)target)[oneItem] is BusinessBase)

{

List<string> itemError = GetErrorInformation(((IBindingList)target)[oneItem], inspectedObjects);

// if we do not have this message already, add it

if (itemError.Count > 0)

{

foreach (String s in itemError)

{

if (!returnValue.Contains(s))

{

returnValue.Add(s);

}

}

}

}

}

}

else if (target is BusinessBase)

{

// run through broken rules collection for this object

foreach (BrokenRule oneBrokenRule in ((BusinessBase)target).BrokenRulesCollection)

{

// if we do not have this message already, add it

if (!returnValue.Contains(oneBrokenRule.Description))

{

returnValue.Add(oneBrokenRule.Description);

}

}

// get list of properties for this object

PropertyInfo[] properties = target.GetType().GetProperties();

foreach (PropertyInfo oneProperty in properties)

{

// get object that sits on this property

if (!oneProperty.PropertyType.IsPrimitive)

{

if (oneProperty.GetIndexParameters().Length == 0)

{

Object propTarget = oneProperty.GetValue(target, null);

// call this procedure resursively to get error for property based object

List<string> propErrorText = GetErrorInformation(propTarget, inspectedObjects);

// if we do not have this message already, add it

if (propErrorText.Count > 0)

{

foreach (String s in propErrorText)

{

if (!returnValue.Contains(s))

{

returnValue.Add(s);

}

}

}

}

}

}

}

}

// strip out duplicate carriage returns before returning the value.

return returnValue;

}

}

RockfordLhotka replied on Wednesday, March 25, 2009

Ahh, sure, that makes sense.

 

The object itself is enumerable, but we didn’t mark the interface that way. I’ll add this to the wish list, as it would make things cleaner if the interface better reflected the nature of the object itself.

 

Rocky

Copyright (c) Marimer LLC