36

Is there any way to validate using DataAnnotations in WPF & Entity Framework?

Matthew Murdoch
  • 30,874
  • 30
  • 96
  • 127
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632

7 Answers7

48

You can use the DataAnnotations.Validator class, as described here:

http://johan.driessen.se/archive/2009/11/18/testing-dataannotation-based-validation-in-asp.net-mvc.aspx

But if you're using a "buddy" class for the metadata, you need to register that fact before you validate, as described here:

http://forums.silverlight.net/forums/p/149264/377212.aspx

TypeDescriptor.AddProviderTransparent(
  new AssociatedMetadataTypeTypeDescriptionProvider(typeof(myEntity), 
    typeof(myEntityMetadataClass)), 
  typeof(myEntity));

List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(myEntity, null, null)
bool valid = Validator.TryValidateObject(myEntity, context, results, true);

[Added the following to respond to Shimmy's comment]

I wrote a generic method to implement the logic above, so that any object can call it:

// If the class to be validated does not have a separate metadata class, pass
// the same type for both typeparams.
public static bool IsValid<T, U>(this T obj, ref Dictionary<string, string> errors)
{
    //If metadata class type has been passed in that's different from the class to be validated, register the association
    if (typeof(T) != typeof(U))
    {
        TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(T), typeof(U)), typeof(T));
    }

    var validationContext = new ValidationContext(obj, null, null);
    var validationResults = new List<ValidationResult>();
    Validator.TryValidateObject(obj, validationContext, validationResults, true);

    if (validationResults.Count > 0 && errors == null)
        errors = new Dictionary<string, string>(validationResults.Count);

    foreach (var validationResult in validationResults)
    {
        errors.Add(validationResult.MemberNames.First(), validationResult.ErrorMessage);
    }

    if (validationResults.Count > 0)
        return false;
    else
        return true;
}

In each object that needs to be validated, I add a call to this method:

[MetadataType(typeof(Employee.Metadata))]
public partial class Employee
{
    private sealed class Metadata
    {
        [DisplayName("Email")]
        [Email(ErrorMessage = "Please enter a valid email address.")]
        public string EmailAddress { get; set; }
    }

    public bool IsValid(ref Dictionary<string, string> errors)
    {
        return this.IsValid<Employee, Metadata>(ref errors);
        //If the Employee class didn't have a buddy class,
        //I'd just pass Employee twice:
        //return this.IsValid<Employee, Employee>(ref errors);
    }
}
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Jeremy Gruenwald
  • 1,947
  • 1
  • 19
  • 18
  • I have a gazillion classes related with MD, do you have any good idea for how to associate it with the MD dynamically, but I want to do it on demand, i.e. only when im going to use this class (or it's validation) and only once. – Shimmy Weitzhandler Aug 10 '10 at 03:07
  • thank you! BTW, no need to mark the dictionary as 'ref', since you're not going to change the reference anyway, just access its properties. – Shimmy Weitzhandler Oct 14 '10 at 06:15
  • @Shimmy - I pass the error dictionary as 'ref' because the IsValid function adds errors to it (initializing first if necessary), and I want to get the results back along with the bool return value. – Jeremy Gruenwald Oct 14 '10 at 17:52
  • I know this is a bit old, but this is pure gold! +1 – MHollis Jul 20 '11 at 06:52
  • 1
    Dictionary is a reference type (not a value type) and is always passed by reference. – JarrettV Aug 10 '11 at 21:58
  • There are two important notes for Silverlight developers here. 1. The technique used in this answer is not supported in Silverlight as the AssociatedMetadataTypeTypeDescriptionProvider class (and others) are not implemented. http://jeffhandley.com/archive/2010/11/16/DataAnnotationsSubset.aspx 2. RIA Services includes validation attributes added to metadata classes in the classes that are generated on the client - which means that this is a non issue on the client when using RIA Services. – Scott Munro Oct 09 '12 at 13:27
4

I think that what is missing from Craigs answer is how to actually check if there are validation errors. This is DataAnnotation validation runner written by Steve Sanderson for those who want to run validation check in deferent layer then presentation (http://blog.codeville.net/category/xval/ , the code is in example project):

public static IEnumerable<ErrorInfo> GetErrors(object instance)
{
    var metadataAttrib = instance.GetType().GetCustomAttributes
        (typeof(MetadataTypeAttribute), true).
            OfType<MetadataTypeAttribute>().FirstOrDefault();
    var buddyClassOrModelClass = 
        metadataAttrib != null ? 
        metadataAttrib.MetadataClassType : 
        instance.GetType();
    var buddyClassProperties = TypeDescriptor.GetProperties
        (buddyClassOrModelClass).Cast<PropertyDescriptor>();
    var modelClassProperties = TypeDescriptor.GetProperties
        (instance.GetType()).Cast<PropertyDescriptor>();

    return from buddyProp in buddyClassProperties
           join modelProp in modelClassProperties
               on buddyProp.Name equals modelProp.Name
           from attribute in buddyProp.Attributes.
               OfType<ValidationAttribute>()
           where !attribute.IsValid(modelProp.GetValue(instance))
           select new ErrorInfo(buddyProp.Name, 
               attribute.FormatErrorMessage(string.Empty), instance);
}

I'm not familiar with WPF (not sure if there is some out-of-the-box solution for you question), but maybe you can use it.

Also, there are some comments on his blog that in some cases it fails to evaluate validation rule properly but it never failed for me.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Misha N.
  • 3,455
  • 1
  • 28
  • 36
3

You might be interested in the BookLibrary sample application of the WPF Application Framework (WAF). It does exactly what you are asking for: using DataAnnotations in WPF & Entity Framework.

jbe
  • 6,976
  • 1
  • 43
  • 34
1

I had the same question and found the following ideas:

Guillaume Gros
  • 341
  • 3
  • 7
0

Use a "buddy class". Number 4 in this how-to.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
0

In .NET 4, there is validation support in Entity-Framework using this extension, check out: http://blogs.msdn.com/adonet/archive/2010/01/13/introducing-the-portable-extensible-metadata.aspx

I am not sure if it does use DataAnnotations tho.

UPDATE
I tried it with VB.NET and it didn't work, I think it only supports C# projects.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
0

I have written a Contributor based validator which includes a DataAnnotation validation contributor and also checks against broken bindings (where the user has entered incorrect type)

http://adammills.wordpress.com/2010/07/21/mvvm-validation-and-type-checking/

Adam Mills
  • 7,719
  • 3
  • 31
  • 47