0

I can't figure out how to "customize" the rules for the [Required] attribute when I stick it to a custom typed property. Code looks like this:

public class MyProp
{
    public Guid Id {get;set;}
    public string Target {get;set;}
}
public class MyType : IValidatableObject
{
    public string Name {get;set;}
    public MyProp Value {get;set;}

    private MyType()
    {
        this.Name = string.Empty;
        this.Value = new MyProp { Id = Guid.Empty, Target = string.Empty };
    }
    public MyType(Guid id) : this()
    {
        this.Value.Id = id;
        // Fill rest of data through magic
    }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if(this.Value.Id == Guid.Empty)
            yield return new ValidationResult("You must fill the property");
    }
}

This model shows up in forms (through its own EditorTemplate) as a textbox with a button which allows for selection from a list (the backing data is a Dynamics CRM 2011 Environment, and this model is actually aimed to represent a lookup attribute).

public class MyModel
{
    // Many props

    [Required] // This one is enforced correctly
    public string MyString {get;set;}

    [Required] // This one isn't
    public MyType MyData {get;set;}

    public MyModel() { this.MyData = new MyType(); }
}

The resulting view shows the field (empty, of course). User can only input data by clicking the field and choosing from a list (a jquery dialog takes care of this, and it already works).

The IValidatableObject interface sounds promising but the code doesn't seem to be ever invoked.

In the controller, I'm simply doing

[HttpPost]
public ActionResult MyAction(FormCollection data)
{
    if (!ModelState.IsValid) return View();
    // magic: handle data
}

What am I missing ? I probably misunderstood the IValidatableObject interface usage ?

Alex
  • 23,004
  • 4
  • 39
  • 73
  • Perhaps this could help you: http://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject – Silvermind Oct 14 '13 at 10:03
  • I forgot the controller part of the code, amended question. Currently trying to implement the linked post idea. – Alex Oct 14 '13 at 10:17

1 Answers1

1

Your controller action should take the view model as parameter instead of weakly typed FormCollection which has absolutely no relation to your model (and its validation rules):

[HttpPost]
public ActionResult MyAction(MyModel model)
{
    if (!ModelState.IsValid) 
    {
        return View();
    }

    // magic: handle model
}

Now the default model binder is going to be invoked in order to bind the view model from the request and evaluate any validation logic you might have in this model.

How do you expect from your code, ASP.NET MVC, to ever know that you are working with this MyModel class? You absolutely never used it in your POST action, so you cannot expect to have any validation on it.

Once you start using view models you should forget about weakly typed collections such as FormCollection and start working with those view models.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • `MyModel` also contains `string` properties which are validated as expected, do non-base types behave differently ? – Alex Oct 14 '13 at 10:32
  • I don't quite understand what you mean. By the way you shouldn't be mixing data annotation attributes (such as `[Required]`) with `IValidatableObject` implementation. Those are mutually exclusive. You should decide which of the two validation approaches to use. – Darin Dimitrov Oct 14 '13 at 10:36
  • Just a side question/remark, but isn't it so that using IValidatableObject works better with custom javascript validation? I remember an answer from you here on StackOverflow regarding jquery.validation.js – Silvermind Oct 14 '13 at 11:28
  • @Silvermind, no `IValidatableObject` doesn't work at all with client side javascript validation. If you use `IValidatableObject` there won't be any javascript validation. You will have to write everything. – Darin Dimitrov Oct 14 '13 at 11:54