By default the CSLA BO’s brokenRulesCollection doesn’t contain child BO’s brokenrules, I tried to create a GetValidationMessages() method in MyBusinessBase class, the difficult I had was to cast and detect the generic type. Thanks to this post, I made it happen by creating an IsDerivedFromGenericType method.
I don’t need to call this: if (child.GetType().IsSubclassOf(typeof(MyBusinessBase<>)))
Or if (child is MyBusinessBase<>)
Actually those two won’t work. This IsDerivedFromGenericType is handy.
if (IsDerivedFromGenericType(child.GetType(), typeof(MyBusinessBase<>) ) )
But it’s too complex, especially I had to call reflection to call this GetValidationMessages recursively. Because I don’t know how to do this:
((MyBusinessBase<>)child).GetBOValidataionMessage()
Instead I had to call reflection:
private string GetBOValidataionMessage(object v)
{
// Would be nice if I can cast to MyBusinessBase
// T is an unknow onject type below, so I had to use reflection to invock method.
// MyBusinessBase bo = (MyBusinessBase)v;
// return bo.GetValidationMessage();
string childValidataionMessage = "";
// invoke method to get validation message from child BO.
// kind of recursive call here.
var flags =
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
MethodInfo method = v.GetType().GetMethod("GetValidationMessage", flags);
if (method != null)
{
childValidataionMessage = (string)method.Invoke(v, null);
}
return childValidataionMessage;
}
In fact, it could be much easier if I just simply override the BrokenRulesCollection property. (Idea from this post.)
public override BrokenRulesCollection BrokenRulesCollection
{
get
{
BrokenRulesCollection brokenRules = BrokenRulesCollection.CreateCollection();
// Get root broken rules.
brokenRules.Merge(this.ToString(), base.BrokenRulesCollection);
foreach (var child in this.FieldManager.GetChildren())
{
if (child is IEnumerable)
{
foreach (var v in (IEnumerable)child)
{
if (v is BusinessBase){
brokenRules.Merge(v.ToString(), ((BusinessBase) v).BrokenRulesCollection);
}
}
}
else
{
if (child is BusinessBase)
{
brokenRules.Merge(child.ToString(), ((BusinessBase)child).BrokenRulesCollection);
}
}
}
return brokenRules;
}
}
// BrokenRule class doesn't have an "Owner" field, which is useful in this kind of
// multi-level object map to locate the actual error message from.
// Fortunately, the rulename contains object name, so I can do this stupid string parse here.
public string ExtractObjectName(string ruleName)
{
string result;
// get namespace
string spacename = this.GetType().Namespace;
// get rid of namespace
result = ruleName.Replace(spacename + ".", "");
// get rid of everything after the dot
int start = result.LastIndexOf("//") + 2;
int end = result.IndexOf(".") ;
result = result.Substring(start, end - start);
return result;
}
public string GetValidationMessage()
{
StringBuilder sb = new StringBuilder();
foreach (var x in this.BrokenRulesCollection)
{
sb.AppendLine(string.Format("{0}.{1}: {2}", ExtractObjectName(x.RuleName),x.Property, x.Description));
}
return sb.ToString();
}