NotNullPropertyValidator

Topics: Validation Application Block
Aug 9, 2007 at 11:07 AM
Edited Aug 9, 2007 at 11:10 AM
Hi! Since there is a great PropertyValidator but it allows only do some comparision based on the functions like Equal LessThan etc...
So I made a NotNullPropertyValidator, witch I would like to see in the next relases.

The idea behind of this is that when some property is not null, you sometimes want to ensure that other property value is also not null.
IE. If production division is selected then there should be also a selected product.
but if there is product selected, it is optional to choose production division.

Example:
public class OrderLine
{
...
private Product _product;
Division _productionDivision;

public virtual Product Product
{
get { return _product; }
set { _product = value;}
}

[NotNullValidator(MessageTemplate = "Production division is missing")]
[NotNullPropertyValidator("Product", MessageTemplate="By selecting a production division, you must select ordered product also")]
public virtual Division ProductionDivision
{
get { return _productionDivision;}
set { _productionDivision = value;}
}
}


And the code for supporting this is:
//================================ NotNullPropertyValidator.cs ===============================================

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Microsoft.Practices.EnterpriseLibrary.Validation.Validators
{
/// <summary>
/// NotNullPropertyValidator is similar to the PropertyComparisionValidator, but it does compare that other property is null
/// </summary>
public class NotNullPropertyValidator:NotNullValidator
{

private PropertyInfo _propertyInfo;
/// <summary>
///
/// </summary>
public NotNullPropertyValidator()
: base(false)
{

}

/// <summary>
/// <para>Initializes a new instance of the <see cref="NotNullValidator"/>.</para>
/// </summary>
/// <param name="propertyInfo">The message template to log failures.</param>
public NotNullPropertyValidator(PropertyInfo propertyInfo)
: base(false)
{

_propertyInfo = propertyInfo;

}


/// <summary>
/// Validates by checking if <paramref name="objectToValidate"/> is not<see langword="null"/>.
/// </summary>
/// <param name="objectToValidate">The object to validate.</param>
/// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
/// <param name="key">The key that identifies the source of <paramref name="objectToValidate"/>.</param>
/// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
protected internal override void DoValidate(object objectToValidate,
object currentTarget,
string key,
ValidationResults validationResults)
{
if (null != objectToValidate)
{
if ((_propertyInfo.GetValue(currentTarget, null) == null) == !Negated)
{
LogValidationResult(validationResults, GetMessage(objectToValidate, key), currentTarget, key);
}
}
}

}
}
//============================= NotNullPropertyValidatorAttribute.cs ==================================================

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Validation.Configuration;
using System.Configuration;
using System.Reflection;
using System.Globalization;
using Microsoft.Practices.EnterpriseLibrary.Validation.Properties;

namespace Microsoft.Practices.EnterpriseLibrary.Validation.Validators
{
/// <summary>
/// Represents a <see cref="NotNullPropertyValidator"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Property
| AttributeTargets.Field
| AttributeTargets.Method
| AttributeTargets.Class
| AttributeTargets.Parameter,
AllowMultiple = true,
Inherited = false)]
public class NotNullPropertyValidatorAttribute : ValueValidatorAttribute
{

private string _propertyToCompare;
/// <summary>
/// <para>Initializes a new instance of the <see cref="NotNullPropertyValidator"/> class.</para>
/// </summary>
public NotNullPropertyValidatorAttribute()
{ }
/// <summary>
///
/// </summary>
/// <param name="propertyToCompare"></param>
public NotNullPropertyValidatorAttribute(string propertyToCompare)

{
_propertyToCompare = propertyToCompare;

}

/// <summary>
/// Creates the <see cref="PropertyComparisonValidator"/> described by the configuration object.
/// </summary>
/// <param name="targetType">The type of object that will be validated by the validator.</param>
/// <param name="ownerType">The type of the object from which the value to validate is extracted.</param>
/// <param name="memberValueAccessBuilder">The <see cref="MemberValueAccessBuilder"/> to use for validators that
/// require access to properties.</param>
/// <returns>The created <see cref="PropertyComparisonValidator"/>.</returns>
protected override Validator DoCreateValidator(Type targetType, Type ownerType, MemberValueAccessBuilder memberValueAccessBuilder)
{
if (string.IsNullOrEmpty(this._propertyToCompare))
{
throw new ConfigurationErrorsException(Resources.ExceptionPropertyToCompareNull);
}
PropertyInfo propertyInfo = ValidationReflectionHelper.GetProperty(ownerType, _propertyToCompare, false);
if (propertyInfo == null)
{
throw new ConfigurationErrorsException(
string.Format(CultureInfo.CurrentUICulture,
Resources.ExceptionPropertyToCompareNotFound,
this._propertyToCompare,
ownerType.FullName));
}

return new NotNullPropertyValidator(propertyInfo);
}
/// <summary>
///
/// </summary>
/// <param name="targetType"></param>
/// <returns></returns>
protected override Validator DoCreateValidator(Type targetType)
{
return new NotNullPropertyValidator();
}


}
}


I know that there is a entlibcontrib project but how the heck do I contribute, is there some kind of SourceControl could'nt find any.
Aug 10, 2007 at 7:00 AM
Thanks for sharing this. I just added you as a developer to the /EntLibContrib project, so you should now be able to check your code into the solution.
The only thing I would suggest is that you may want to come up with a better name for this validator, as (to me at least) it isn't clear that the behaviour is to check that the "nullness" of the referenced property is the same as the "nullness" of the property that the validator is attached to. Let me know if this is correct, but I imagined that in a situation where property A has this validator applied to it, and the validator points to property B, then the expected results are:
  • A = null, B = null --> Valid
  • A = not null, B = not null --> Valid
  • A = not null, B = null --> Invalid
  • A = null, B = not null --> Invalid (??)

I accept that this is a difficult concept to convey in a class name, but how about something like PropertyNullComparisonValidator, since it's kind of like the built-in PropertyComparisionValidator?

Tom
Aug 10, 2007 at 12:26 PM

tomhollander wrote:

  • A = null, B = null --> Valid
  • A = not null, B = not null --> Valid
  • A = not null, B = null --> Invalid
  • A = null, B = not null --> Invalid (??)

I accept that this is a difficult concept to convey in a class name, but how about something like PropertyNullComparisonValidator, since it's kind of like the built-in PropertyComparisionValidator?



Tom


Yes, this name is better one, I'll do the refactoring.


tomhollander wrote:
  • A = null, B = not null --> Invalid (??)
Tom


No if A = null, B = not null --> Valid, beacause A is the controller property. The idea behind of this is, when A has some value, then B must also be selected, if B is selected don't bother with it.

so there are only 3 versions:
  • A = null, B = null --> Valid
  • A = not null, B = not null --> Valid
  • A = not null, B = null --> Invalid


If you want
  • A = null, B = not null --> Invalid (??)
then you have to use
  • B = not null, A = null --> Invalid

like

public virtual Product A
{
get { return _product; }
set { _product = value;}
}

[NotNullPropertyValidator("A", MessageTemplate="A property cannot be null if B is not null")]
public virtual Division B
{
get { return _productionDivision;}
set { _productionDivision = value;}
}
}


Aug 14, 2007 at 3:49 PM
Hmmm, just thinking out loud here, but implementation aside, this looks like a use of the validation block to express a more complicated business rule than the default validators.

With that in mind, has anyone ever considered integrating a business rules engine (e.g. http://NxBRE.org ) with the validation block?
Aug 15, 2007 at 10:43 AM
Well, I think that Validation stands for validating the business object.

In my app the business layer with the business objects is in separated dll and validation block does only very trivial validations and notify the user that this string is too long or if you select this you should select the other "thing" also.
More complicated calculations I'd put in Stored Procedure.

If I really really need some extra flexibility for business calculations I'd use workflow foundation, but I never needed this, because found out that business rules does not change as often as the technology does :)