title: Writing a custom attribute with an infinite number of matching parameters
body: <p>I've written an If-IsRequired custom attribute to validate that a property contains a value depending on the values of some other properties in the model. Since I want to make this attribute apply to as many situations as possible, I want to allow the option for the developer leveraging the attribute to supply an infinite number of matched parameters. And lastly, I want to be able to enforce that all the parameters are matched correctly.</p>
  
  <p>This is what I've written thus far. While I'm currently using arrays of strings, I'd be perfectly happy to use some sort of collection, which been unable to work. In addition, I now have a need to support the current attribute definition and create a new overload that includes the comparison operator. This will allow me to make less than, greater than, and not equal comparisons in addition to the original definition which just assumes all comparisons are done with equals.</p>
  
  <pre class="lang-cs prettyprint-override"><code>    /// &lt;summary&gt;
      /// A custom attribute that checks the value of other properties passed to it in order to
      /// determine if the property this attribute is bound to should be required.
      /// &lt;/summary&gt;
      [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
      public class IsPropertyRequiredAttribute : ValidationAttribute
      {
          private const string DefaultErrorMessage = "{0} is required.";
  
          public string[] _selectionContextNames { get; private set; }
          public string[] _expectedValues { get; private set; }
  
          /// &lt;summary&gt;
          /// Creates a new instance of the IsPropertyRequriedAttribute.
          /// &lt;/summary&gt;
          /// &lt;param name="SelectionContextNames"&gt;The name of the other property in the view model to check the value of.&lt;/param&gt;
          /// &lt;param name="ExpectedValues"&gt;The expected value of the other property in the view model in order to determine if the current property the attribute is bound to should be required.&lt;/param&gt;
          public IsPropertyRequiredAttribute(string[] SelectionContextNames, string ExpectedValues)
              : base(DefaultErrorMessage)
          {
  
              _selectionContextNames = SelectionContextNames;
              _expectedValues = ExpectedValues;
          }
  
          public override bool IsValid(object value)
          {
              if (_selectionContextNames == null || _expectedValues == null)
              {
                  if (_selectionContextNames != null || _expectedValues != null)
                  {
                      string paramName;
                      if (_selectionContextNames == null)
                      {
                          paramName = "ExpectedValues";
                      }
                      else
                      {
                          paramName = "SelectionContextNames";
                      }
  
                      throw new ArgumentException("Key/Value pairs need to match for IsPropertyRequired.", paramName);
                  }
              }
              else if (_selectionContextNames.Length != _expectedValues.Length)
              {
                  string paramName;
                  if (_selectionContextNames.Length &lt; _expectedValues.Length)
                  {
                      paramName = "ExpectedValues";
                  }
                  else
                  {
                      paramName = "SelectionContextNames";
                  }
  
                  throw new ArgumentException("Parameter element counts need to match for IsPropertyRequired.", paramName);
              }
  
              bool paramsValid = true;
  
              if (_selectionContextName!= null)
              {
                  for (int i = 0; i &lt; _selectionContextName.Length; i++)
                  {
                      string paramValue = HttpContext.Current.Request[_selectionContextName[i]];
  
                      if (_expectedValue[i] != paramValue)
                      {
                          paramsValid = false;
                      }
                  }
  
                  if (paramsValid == true)
                  {
                      return (value != null);
                  }
                  else
                  {
                      return true;
                  }
              }
              else
              {
                  return true;
              }
          }
  
          public override string FormatErrorMessage(string name)
          {
              return String.Format(DefaultErrorMessage, name);
          }
      }
  </code></pre>
  
  <p>While using the attribute to decorate the property will depend on how the attribute is defined, this is what I have currently implemented (which could also probably be improved):</p>
  
  <pre class="lang-cs prettyprint-override"><code>[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4"}, new string[] {"1", "2", "3", "4"})]
  public string SomeText { get; set; }
  </code></pre>
  
  <p>Also, I want to prevent, as much as I can, the following decoration from happening:
  </p>
  
  <pre><code>[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4", "prop5withoutvalue"}, new string[] {"1", "2", "3", "4"})]
  public string SomeOtherText { get; set; }
  </code></pre>
  
  <p>And with the new overload including comparison operators as a parameter, we could now have:
  </p>
  
  <pre><code>[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4"}, new string[] {"==", "&gt;", "!=", "&lt;="}, new string[] {"1", "2", "3", "4"})]
  public string SomeComparisonText { get; set; }
  </code></pre>