1

I have an Address class that is used for both a MailingAddress and BillingAddress property in my Model. I want the MailingAddress to be required, but not the BillingAddress, but am not seeing a way to do this with DataAnnotations.

If I were able to set the [Required] attribute on the MailingAddress property and somehow define the logic for how the Address class is supposed to handle the required logic, I feel like that would be a simple solution.

Any ideas?

Scott Scowden
  • 1,155
  • 2
  • 11
  • 19

3 Answers3

1

If your question is how to use the Required attribute in your own logic, the answer is by use of reflection. Forgive me if that is not your question.

Get all properties from the type in question, then see if it is decorated with a RequiredAttribute or not.

class ParentClass
{
      [Required]
      public Address MailingAddress { get; set; }

      public Address BillingAddress { get; set; }
}

(...)

Type t = typeof(ParentClass);

foreach (PropertyInfo p in t.GetProperties())
{
    Attribute a = Attribute.GetCustomAttribute(p, typeof(RequiredAttribute));
    if (a != null)
    {
          // The property is required, apply your logic
    }
    else
    {
          // The property is not required, apply your logic
    }
}

Edit: Fixed a typo in code

Edit 2: Extended code example

havardhu
  • 3,576
  • 2
  • 30
  • 42
  • Thank you for your response, but I don't think that's what I am really looking for. I need to be able to have my Address class be required, but only for the MailingAddress property in the parent class it is consumed in and not for the BillingAddress property. – Scott Scowden Sep 01 '11 at 00:28
  • It's not the class that is required, but the MailingAddress property. Then you could decorate MailingAddress with [Required] in the parent class, and, instead of using typeof(Address) you'd use typeof() as shown above – havardhu Sep 01 '11 at 00:33
0

This is just an odd quirk which popped into my head:

A simple solution might be to subclass Address to OptionalAddress.

I don't think the Required attributes would be inherited to the child class.

[AttributeUsage (Inherited = False)] also comes to mind if needed.

A more MVCish solution might be to implement a custom model binder (completely untested):

public override object BindModel(ControllerContext controllerContext,
    ModelBindingContext bindingContext)
        {
            var address = base.BindModel(controllerContext, bindingContext) as Address;
            if (bindingContext.ModelName.EndsWith("BillingAddress"))
            {
                foreach (PropertyInfo p in address.GetType().GetProperties())
                {
                Attribute a = Attribute.GetCustomAttribute(p, typeof(RequiredAttribute));
                if (a != null 
                    && propertyInfo.GetValue(address, null) == null 
                    && bindingContext.ModelState[bindingContext.ModelName 
                       + "." + p.Name].Errors.Count == 1)
                {
                    bindingContext.ModelState[bindingContext.ModelName + "." + p.Name].Errors.Clear();
                }
            }
            return address;
        }
Kevin Stricker
  • 17,178
  • 5
  • 45
  • 71
  • RequiredAttribute is only applicable to properties, parameters and fields. You do not say that a class is required, you say that one of its properties is. – havardhu Sep 01 '11 at 00:39
  • I think OP wants to know how to say the properties of one instance of a class are required but of another instance are not... The two instances being properties in the same parent object. – Kevin Stricker Sep 01 '11 at 00:46
  • Yeah that is how I understand it as well. The way I see it, though, sub-classing is not necessary, it is enough to decorate the property that is required with [Required]. My edited answer above reflects this. – havardhu Sep 01 '11 at 00:57
  • I think with a required address in MVC you want to make sure some properties eg: Zip code are also required. I don't see your answer handling that case. – Kevin Stricker Sep 01 '11 at 01:01
  • Yes, then sub-classing would be one way to go. I didn't see this specified in the OP but re-reading the last sentence I guess it can be interpreted that way; writing logic for how the Address itself should behave when it is required. – havardhu Sep 01 '11 at 01:14
  • 1
    Lets not forget IValidateableObject? – Adam Tuliper Sep 01 '11 at 14:03
  • Good call: http://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject – Kevin Stricker Sep 01 '11 at 16:52
0

Many options available at this previously asked question:

ASP.NET MVC Conditional validation

Do you need this validation done on the client side or not?

IValidateableObject will be used in conjunction with any of your existing attributes and can provide for the additional custom validation.

Community
  • 1
  • 1
Adam Tuliper
  • 29,982
  • 4
  • 53
  • 71