EitherOrRequired rule

EitherOrRequired rule

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


Kevin Fairclough posted on Thursday, August 25, 2011

Hi

Something simple which I cannot get to work :-)

I need a rule that will examine two properties and error if both are blank.  I have created this rule so it takes two IPropertInfo objects.

public class EitherHaveValue : CommonBusinessRule
    {
        IPropertyInfo SecondaryProperty {getset;}

        /// <summary>
        /// Creates an instance of the rule.
        /// </summary>
        /// <param name="primaryProperty">Property to which the rule applies.</param>
        public EitherHaveValue(IPropertyInfo primaryProperty, IPropertyInfo secondaryProperty)
            : base(primaryProperty)
        {
            PrimaryProperty = primaryProperty;
            SecondaryProperty = secondaryProperty;
            //this.AffectedProperties.Add(PrimaryProperty);
            this.AffectedProperties.Add(SecondaryProperty);
           
            InputProperties = new List<IPropertyInfo> { primaryProperty, secondaryProperty };
        }

        /// <summary>
        /// Rule implementation.
        /// </summary>
        /// <param name="context">Rule context.</param>
        protected override void Execute(RuleContext context)
        {
            var value = context.InputPropertyValues[PrimaryProperty];
            var value2 = context.InputPropertyValues[SecondaryProperty];

#if WINDOWS_PHONE
      if (value == null || string.IsNullOrEmpty(value.ToString()))
#else
            if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
#endif
            {
#if WINDOWS_PHONE
                if (value2 == null || string.IsNullOrEmpty(value2.ToString()))
#else
                if (value2 == null || string.IsNullOrWhiteSpace(value2.ToString()))
#endif                
                {

                    var message =
                        string.Format(PrimaryProperty.FriendlyName + " or " + SecondaryProperty.FriendlyName +
                                      " required.");
                    context.Results.Add(new RuleResult(RuleName, PrimaryProperty, message) {Severity = Severity});
                    //context.Results.Add(new RuleResult(RuleName, SecondaryProperty, message) {Severity = Severity});
                }
            }
        }
    }

I can only get this to work properly by adding it twice to the BusinssRules property in the BO:
BusinessRules.AddRule(new Csla.Extensions.Rules.EitherHaveValue(TypeProperty, ValueProperty));
BusinessRules.AddRule(new Csla.Extensions.Rules.EitherHaveValue(ValueProperty, TypeProperty));

Doing this however gives me two errors instead of one?
I'm obviously doing this wrong, any ideas?

Regards
Kevin Fairclough

JonnyBee replied on Thursday, August 25, 2011

Hi,

Here is the AnyRequired rule from CslaContrib:

Remember to add a Dependency rule from field2 to field1 to make the rule run also when field2 is changed.

      BusinessRules.AddRule(new AnyRequired(TypeProperty, ValueProperty, Value2Property));
      BusinessRules.AddRule(new Dependency(ValueProperty, TypeProperty));

The AnyRequired rule:

 /// <summary>
  /// Check that at least one of the fields of type string or smartvalue field has a value.
  /// Code must also add Dependency rules from each additional properties to primary property.
  /// </summary>
  public class AnyRequired : CommonBusinessRule
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="AnyRequired"/> class.
    /// </summary>
    /// <param name="primaryProperty">The primary property.</param>
    /// <param name="additionalProperties">The additional properties.</param>
    public AnyRequired(IPropertyInfo primaryProperty, params IPropertyInfo[] additionalProperties)
      : base(primaryProperty)
    {
      if (InputProperties == null)
        InputProperties = new List<IPropertyInfo>();
      InputProperties.AddRange(additionalProperties);
    }
 
    /// <summary>
    /// Initializes a new instance of the <see cref="AnyRequired"/> class.
    /// </summary>
    /// <param name="primaryProperty">The primary property.</param>
    /// <param name="errorMessageDelegate">The error message function.</param>
    /// <param name="additionalProperties">The additional properties.</param>
    public AnyRequired(IPropertyInfo primaryProperty, Func<string> errorMessageDelegate, params IPropertyInfo[] additionalProperties)
      : this(primaryProperty, additionalProperties)
    {
      ErrorMessageDelegate = errorMessageDelegate;
    }
 
    /// <summary>
    /// Gets the error message.
    /// </summary>
    /// <value></value>
    protected override string ErrorMessage
    {
      get
      {
        return HasErrorMessageDelegate ? base.ErrorMessage : CslaContrib.Properties.Resources.AnyRequiredRule;
      }
    }
 
    /// <summary>
    /// Executes the rule in specified context.
    /// </summary>
    /// <param name="context">The context.</param>
    protected override void Execute(RuleContext context)
    {
      foreach (var field in context.InputPropertyValues)
      {
        // smartfields have their own implementation of IsEmpty
        var smartField = field.Value as ISmartField;
 
        if (smartField != null)
        {
          if (!smartField.IsEmpty) return;
        }
        else if (field.Value != null && !field.Value.Equals(field.Key.DefaultValue)) return;
      }
 
      var fields = context.InputPropertyValues.Select(p => p.Key.FriendlyName).ToArray();
      var fieldNames = String.Join(", ", fields);
      context.AddErrorResult(string.Format(ErrorMessage, fieldNames));
    }
  }

 

Kevin Fairclough replied on Friday, August 26, 2011

Thanks Jonny 

This works however the dependant property doesn't get an error adorner in our WPF, only the primary property.

Thanks

Kevin

JonnyBee replied on Friday, August 26, 2011

Hi,

There's been a lot of discussions here on whether to set error/warnin etc on other fields.

Truth is - this has not worked as expected up until now and has been fixed i Csla 4.2 (trunk only) so that f.ex an AnyRequired rule can

So when you move to Csla 4.2 this will be possible within just one rule.

Copyright (c) Marimer LLC