I\'m using VS2008 SP1, WCF Ria Service July 2009 CTP. I found out that MetadataType does not work in partial class mode, really don\'t know what I have missed out:
W
If you are working with WPF and EF, this has always worked for me...
[MetadataType(typeof(Department.Metadata))]
public partial class Department : BaseModel
{
static Department()
{
TypeDescriptor.AddProvider(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Department),typeof(Metadata)), typeof(Department));
}
private sealed class Metadata
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Department Name is required.")]
[StringLength(50, ErrorMessage = "Name must be between 3 and 50 characters.", MinimumLength = 3)]
public string Name;
[StringLength(250, ErrorMessage = "Name must be between 10 and 250 characters.", MinimumLength = 10)]
public string Description;
}
}
And the base class that makes it happen...
public abstract class BaseModel : IDataErrorInfo
{
#region Validation
string IDataErrorInfo.Error
{
get { return null; }
}
string IDataErrorInfo.this[string propertyName]
{
get
{
var propertyInfo = GetType().GetProperty(propertyName);
var results = new List<ValidationResult>();
var result = Validator.TryValidateProperty(propertyInfo.GetValue(this, null), new ValidationContext(this, null, null)
{
MemberName = propertyName
}, results);
if (result) return string.Empty;
var validationResult = results.First();
return validationResult.ErrorMessage;
}
}
#endregion
}
Many thanks to Jeremy Gruenwald for the answer above... I was completely stuck on this one.
I wanted to create a standard validation class based on this solution, but I didn't want to have to pass in the metadata class type because it just felt ugly.
To achieve this I created a static class which does a lookup on the custom attributes to get the metadata class type and then registers that class before returning the validation results.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace MyApp.Validation
{
public static class EntityValidator
{
public static List<ValidationResult> Validate(object instance, bool validateAllProperties = true)
{
RegisterMetadataClass(instance);
var validationContext = new ValidationContext(instance, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(instance, validationContext, validationResults, validateAllProperties);
return validationResults;
}
private static void RegisterMetadataClass(object instance)
{
var modelType = instance.GetType();
var metadataType = GetMetadataType(modelType);
if (metadataType != null)
{
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(modelType, metadataType), modelType);
}
}
private static Type GetMetadataType(Type type)
{
var attribute = (MetadataTypeAttribute)type.GetCustomAttributes(typeof (MetadataTypeAttribute), true).FirstOrDefault();
return attribute == null ? null : attribute.MetadataClassType;
}
}
}
usage is a simple as:
var errors = EntityValidator.Validate(myEntity);
EDIT: I found the answer here: http://forums.silverlight.net/forums/p/149264/377212.aspx
Before validating, you need to manually register the metadata class:
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(typeof(Person), typeof(PersonMetadata)), typeof(Person));
List<ValidationResult> res = new List<ValidationResult>();
bool valid = Validator.TryValidateObject(p, new ValidationContext(p, null, null), res, true);
(Original answer follows)
The problem isn't specifically with your partial class, it's that Validator.TryValidateObject doesn't seem to recognize the MetaDataType attribute. I have the same problem - the built-in validation in MVC 2 recognizes the metadata class, but TryValidateObject doesn't.
See these: Validating DataAnnotations with Validator class Validation does not work when I use Validator.TryValidateObject
As a side note, I don't know if it's necessary, but all examples I've seen for metadata classes employ the default get/set on each property:
[Required(AllowEmptyStrings=false, ErrorMessage="Name required entry")]
[StringLength(3)]
public string Name { get; set; }