Enterprise Library ValueValidator for NHibernate unique values

Mouthful of a title...  So the background story is the need to check uniqueness of a value against the existing database of values during the Enterprise Library (v4.0) validation (and before the DB constraint violation), oh and I'm using NHibernate (v2.0) for persistence.  I decided to to create a ValueValidator to allow the application of rule by config or attribute rather than implement as self validation. Straight to it then. I will list the full class later but to help explain the cocky bit these are the main methods:

private bool IsUnique(Type targetType, string propertyName, object propertyValue, object validatedObject)
{
    using (ISession session = m_factory.OpenSession())
    {
        int matches = (int)session.CreateCriteria(targetType)
            .Add(Expression.Eq(propertyName, propertyValue))
            .Add(Expression.Not(EqualIdentifier(targetType, validatedObject)))
            .SetProjection(Projections.ProjectionList()
                .Add(Projections.RowCount()))
            .UniqueResult();
        return (matches == 0);
    }
}
private SimpleExpression EqualIdentifier(Type targetType, object validatedObject)
{
    ISessionFactoryImplementor factoryImplementor = (ISessionFactoryImplementor)m_factory;
    IEntityPersister persister = factoryImplementor.GetEntityPersister(targetType);
    object idPropertyValue = persister.GetIdentifier(validatedObject, EntityMode.Poco);
    string idPropertyName = persister.IdentifierPropertyName;
    return Expression.Eq(idPropertyName, idPropertyValue);
}

The IsUnique method is called within the DoValidate of the ValueValidator, it constructs a criteria query to basically select count(*) from table where unique property = value and not key column = key value - with the identifier detail returned as a SimpleExpression from the  EqualIdentifier method using the objects persister.  Actually pretty simple and sweet!  Full code for the validator:

public class UniqueValueValidator : ValueValidator
{
    private ISessionFactory m_factory;
    public UniqueValueValidator(string messageTemplate, string tag, bool negated)
        : base(messageTemplate, tag, negated)
    {
        Configuration config = new Configuration().Configure();
        m_factory = config.BuildSessionFactory();
    }
    protected override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
    {
        Type targetType = GetTargetType(currentTarget);
        object target = GetTargetObject(currentTarget);
        if (!IsUnique(targetType, key, objectToValidate, target))
            validationResults.AddResult(new ValidationResult(key + " must be unique", currentTarget, key, base.Tag, this));
    }
    private bool IsUnique(Type targetType, string propertyName, object propertyValue, object validatedObject)
    {
        using (ISession session = m_factory.OpenSession())
        {
            int matches = (int)session.CreateCriteria(targetType)
                .Add(Expression.Eq(propertyName, propertyValue))
                .Add(Expression.Not(EqualIdentifier(targetType, validatedObject)))
                .SetProjection(Projections.ProjectionList()
                    .Add(Projections.RowCount()))
                .UniqueResult();
            return (matches == 0);
        }
    }
    private SimpleExpression EqualIdentifier(Type targetType, object validatedObject)
    {
        ISessionFactoryImplementor factoryImplementor = (ISessionFactoryImplementor)m_factory;
        IEntityPersister persister = factoryImplementor.GetEntityPersister(targetType);
        object idPropertyValue = persister.GetIdentifier(validatedObject, EntityMode.Poco);
        string idPropertyName = persister.IdentifierPropertyName;
        return Expression.Eq(idPropertyName, idPropertyValue);
    }
    private Type GetTargetType(object currentTarget)
    {
        if (currentTarget == null)
            throw new ArgumentException("Target should not be null");
        if (currentTarget is IValidationIntegrationProxy)
            return (currentTarget as IValidationIntegrationProxy).ValidatedType;
        return currentTarget.GetType();
    }
    private object GetTargetObject(object currentTarget)
    {
        if (currentTarget == null)
            throw new ArgumentException("Target should not be null");
        if (currentTarget is IValidationIntegrationProxy)
            return (currentTarget as IValidationIntegrationProxy).GetRawValue();
        return currentTarget;
    }
    protected override string DefaultNegatedMessageTemplate
    {
        get { return "The value must be unique"; }
    }
    protected override string DefaultNonNegatedMessageTemplate
    {
        get { return "The value must be unique"; }
    }
}

and for the attribute:

public class UniqueValueValidatorAttribute : ValueValidatorAttribute
{
    protected override Validator DoCreateValidator(Type targetType)
    {
        return new UniqueValueValidator(MessageTemplate, Tag, Negated);
    }
}
Which means that you can add the UniqueValueValidator attribute to properties to enforce unique value during validation:
        [UniqueValueValidator]
        public virtual string Name { get; set; }
Comments are closed