How to Retrieve Data Annotations from Code? (Programmatically)

How to retrieve Data Annotations from code? (programmatically)

Extension method:

public static T GetAttributeFrom<T>(this object instance, string propertyName) where T : Attribute
{
var attrType = typeof(T);
var property = instance.GetType().GetProperty(propertyName);
return (T)property .GetCustomAttributes(attrType, false).First();
}

Code:

var name = player.GetAttributeFrom<DisplayAttribute>(nameof(player.PlayerDescription)).Name;
var maxLength = player.GetAttributeFrom<MaxLengthAttribute>(nameof(player.PlayerName)).Length;

Get Data Annotations attributes from model

This is the universal way how to do this:

private string GenerateValidationModel<T>()
{
var name = typeof(T).Name.Replace("Model", "ViewModel");
name = Char.ToLowerInvariant(name[0]) + name.Substring(1);

var validationModel = "var " + name + " = {\n";

foreach (var prop in typeof(T).GetProperties())
{
object[] attrs = prop.GetCustomAttributes(true);
if (attrs == null || attrs.Length == 0)
continue;

string conds = "";

foreach (Attribute attr in attrs)
{
if (attr is MinLengthAttribute)
{
conds += ", minLength: " + (attr as MinLengthAttribute).Length;
}
else if (attr is RequiredAttribute)
{
conds += ", required: true";
}
// ...
}

if (conds.Length > 0)
validationModel += String.Format("\t{0}: ko.observable().extend({{ {1} }}),\n", prop.Name, conds.Trim(',', ' '));
}

return validationModel + "};";
}

Usage:

string validationModel = GenerateValidationModel<LoginModel>();

Output:

var loginViewModel = {
UserName: ko.observable().extend({ minLength: 3, required: true}),
Password: ko.observable().extend({ required: true}),
};

It's good idea to cache the output

Adding and Removing Data Annotations from code

It is impossible to add, remove or modify DataAnnotations dynamically since they are Attributes. Attributes are part of the type and can't be changed during runtime.

You could use ModelState as Larsenal suggested provided that:

  • you use it After validation has executed. (prior to that, ModelState will be empty. It doesn't provide access to all validators, it only stores validator-errors after they've occurred)
  • you don't have any clientside validation that's based on the DataAnnotationValidators and fires errors that prevent you from even reaching the serverside validation.

Get parameter's value of a Data Annotation

You need to use reflection to get the attribute applied to your controller action. Below is a quick and dirty example of how you could do that:

    private string GetPermissionFilerValue()
{
object[] attributes = typeof(YourControllerType).GetType().GetMethod("Index").GetCustomAttributes(typeof (PermissionFilterAttribute));

return attributes[0].Roles;
}

Basically you need to get a reference to your controller's type and then from that, get a reference to the method on your controller. Once you have that, in the form a MethodInfo object, you can use GetCustomAttributes to get all custom attributes applied to your method or all custom attributes of a specific type. Once you have your attribute instance you can inspect the Roles property.

As I mentioned, the example above is a very quick and dirty demonstration of how to get an attribute instance for a specific method. You'll likely need to tailor it to fit it into your specific scenario.

How to retrieve GroupName data annotation from ModelMetadata

GroupName is not parsed by the DataAnnotationsModelMetadataProvider. So there's no way to get it right off the ModelMetadata object, even with an extension method.

You could implement your own provider that extends the existing one to add support for GroupName, which Brad Wilson explains in his blog.

You could also write your own attribute instead of using Display(GroupName = ) and implement the IMetadataAware interface to add the groupname to ModelMetadata.AdditionalValues.

Data-Annotations - EntityValidation removal based on condition in code

That is not possible with the existing RequiredAttribute.

However, you can implement your own custom conditional validation attribute.

Here are some links to guide you in the right direction:

http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx
http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
http://blogs.msdn.com/b/simonince/archive/2011/09/29/mvc-validationtookit-alpha-release-conditional-validation-with-mvc-3.aspx

Once you implement your custom RequiredIf conditional validation attribute you can set the condition like:

public class ValidationSample
{
[RequiredIf("PropertyValidationDependsOn", true)]
public string PropertyToValidate { get; set; }

public bool PropertyValidationDependsOn { get; set; }
}

How to catch DataAnnotations Validation in MVVM


the solution

public class Person : IDataErrorInfo // Represents person data.
{
/// <summary>
/// Gets or sets the person's first name.
/// </summary>
/// <remarks>
/// Empty string or null are not allowed.
/// Allow minimum of 2 and up to 40 uppercase and lowercase.
/// </remarks>
[Required]
[RegularExpression(@"^[a-zA-Z''-'\s]{2,40}$")]
public string FirstName{ get; set;}

/// <summary>
/// Gets or sets the person's last name.
/// </summary>
/// <remarks>
/// Empty string or null are not allowed.
/// </remarks>
[Required]
public string LastName { get; set;}

public int Age{ get; set;}

public string Error // Part of the IDataErrorInfo Interface
{
get { throw new NotImplementedException(); }
}

string IDataErrorInfo.this[string propertyName] // Part of the IDataErrorInfo Interface
{
get { return OnValidate(propertyName); }
}

/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentException("Invalid property name", propertyName);

string error = string.Empty;
var value = this.GetType().GetProperty(propertyName).GetValue(this, null);
var results = new List<ValidationResult>(1);

var context = new ValidationContext(this, null, null) { MemberName = propertyName };

var result = Validator.TryValidateProperty(value, context, results);

if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}

return error;
}
}

thanks to Rachel for here hint
and to this link which was very enlightened



Related Topics



Leave a reply



Submit