views:

1098

answers:

3

I'm trying to use DataAnnotations to add validation to my models in asp.NET MVC 2 RC2, using TryUpdateModel

        var user = UserManager.Find(id);

        this.TryUpdateModel<IProvisioningObject>(user, form.ToValueProvider());

This updates the model, but the validation is never called. I tried using TryUpdateModel as well (which is the direct type of user), not using the form value provider, using ProvisioningObject directly (which has the validation metadata), to no avail.

Googling for examples only gives me ways to use DataAnnotations by binding through a parameter

public ActionResult Update(User user)

Which I dislike for update scenarios.

Any tips and/or solutions?

EDIT My objects are auto-generated objects from a WCF service.

I made partials to be able to add DataAnnotations. I call TryUpdateModel three times because it apparently doesn't support inheritance, which I think is also my problem with DataAnnotations. I specify the validation attributes for ProvisioningObject, and the binding doesn't look for inherited stuff like that.

[MetadataType(typeof(ProvisioningObjectMetadata))]
public partial class ProvisioningObject : IProvisioningObject
{
    public string DisplayNameInvariant { get { return string.IsNullOrEmpty(this.DisplayName) ? this.Name : this.DisplayName; } }
}


[MetadataType(typeof(UserMetadata))]
public partial class User : IUser
{
}


public class ProvisioningObjectMetadata
{
    [DisplayName("Country")]
    public string CountryIsoCode { get; set; }

    [Required(ErrorMessageResourceType = typeof(Properties.Validation), ErrorMessageResourceName = "DisplayNameIsRequired")]
    [TempValidator]
    public string DisplayName { get; set; }
}


public class UserMetadata
{
    [DisplayName("Username")]
    public string Name { get; set; }
}


// Controller action
    public ActionResult Update(string id, FormCollection form)
    {
        var user = UserManager.Find(id);

        this.TryUpdateModel<IUser>(user.User, form.ToValueProvider());
        this.TryUpdateModel<IPerson>(user.User, form.ToValueProvider());
        this.TryUpdateModel<IProvisioningObject>(user.User, form.ToValueProvider());

        if (ModelState.IsValid) // always true
        {
            return Redirect;
        }
        else
        {
            return View();
        }
    }

If I add the metadata for DisplayName in UserMetadata, it works as expected, but that seems very redundant for nothing. And it would mean I would also have to copy/paste all my inherited interfaces so TryUpdateModel behaves appropriately.

I guess I'm looking for a way that doesn't require me to copy and paste my validation attributes to inherited classes.

A: 

New Answer:

"My objects are auto-generated objects from a WCF service."

Autogenerated objects won't have any attributes on them. Are you defining your objects and their attributes on the server side or on the client side?

Old Answer: If your metadata is not on IProvisioningObject then no validation will be called. The MVC2 default model binder only knows how to find "extra" [MetadataType(buddyClass)] validation information.

For update scenarios bind against DTOs and then map the DTOs, if IsValid() to your main model classes.

jfar
I just tried to do UpdateModel<ProvisioningObject> (since I can't put MetadataType on an interface) and it doesn't change anything. I created a temporary validator and it doesn't seem to ever hit the IsValid method.For the Update scenario, I'm not sure I follow you. If you have good links to blogs or examples, that would be enough to get me started. Thanks for you input!
Sefyroth
Can you post more code please? I'm not clear on what your trying to to anymore.
jfar
New Answer comment: The attributes are client-side. I use partial classes to add the proper attributes to the auto-generated objects.
Sefyroth
A: 

How do you know that the validation is not being called? Are you checking ModelState.IsValid in your update controller and finding that it is erroneously coming back true?

A typical update pattern is:

UpdateModel(model);
if(!ModelState.IsValid) return View(model);
return RedirectToAction("Index");

If you are expecting some "IsValid" on your model to automatically be called, that will not happen. The data annotations work behind the scenes with the ModelState dictionary on the Controller base class.

kmehta
ModelState.IsValid is always true, even if I put a custom validation attribute that always returns false. A breakpoint in that validation attribute is never hit either.It works if I put the validation right on the type and not the interface. I'm going to paste more code in a second.
Sefyroth
Have you tried placing your metadata type attribute on your class declaration? Example:interface IDataObject { ... contains dataannotations ...}[MetadataType(typeof(IDataObject))]class DataObject { .... }
kmehta
@Kabir MehtaSame problem. It works if I put the DataAnnotations right on the interface, but I don't want to duplicate the validation from the parent class/interface.
Sefyroth
+1  A: 

Implement IDataErrorInfo interface in your partial class You will have to write custom validation for each field(where you can use data annotation class to validate each required property)

If you need code example then let me know. I will write it for you!

source: http://www.asp.net/(S(pdfrohu0ajmwt445fanvj2r3))/learn/mvc/tutorial-37-cs.aspx

scorpio