In a layered application, which a service layer waiting for commands and a Web application (asp.net MVC 3) issuing commands, I have :
// In the service layer
public class MyCommand
{
public string MyVal { get; set;}
public string AnotherProperty { get; set;}
}
// In the web layer
public class MyCommandViewModel
{
public string MyVal { get; set;}
}
The viewmodel class is matching the user form, while the command class is matching the business layer requirements. As the form does not allow users to input AnotherProperty, I didn't include this property in the viewmodel.
Now I have a business rules that requires the MyVal property of my command is required and shouldn't takes more than 20 characters. In such layered application, what are guidelines for implementing such validations ?
I see several possibilities :
1. using System.ComponentModel.DataAnnotations on both classes
// In the service layer
public class MyCommand
{
[Required]
[StringLength(20)]
public string MyVal { get; set;}
public string AnotherProperty { get; set;}
}
// In the web layer
public class MyCommandViewModel
{
[Required]
[StringLength(20)]
public string MyVal { get; set;}
}
this method is working, but I have to "duplicate" the validation attributes. If tomorrow, I'll have to allow 40 chars, I'll have to "find and hope none is missing" all instances of StringLength.
2. using System.ComponentModel.DataAnnotations.MetadataType
The idea here, is to extract the rule in an interface (thanks to Erik Philips for the idea)
// In the service layer
public class IMyCommandMetaData
{
[Required]
[StringLength(20)]
public string MyVal { get; set;}
}
[MetadataType(typeof(IMyCommandMetaData))]
public class MyCommand
{
public string MyVal { get; set;}
public string AnotherProperty { get; set;}
}
// In the web layer
[MetadataType(typeof(IMyCommandMetaData))]
public class MyCommandViewModel
{
public string MyVal { get; set;}
}
this approach is also working. The pro is I define only once the rules, the con is that the MetadataType requires to have all properties defined on the attached object. I have either to implement all properties in my viewmodel, even if not used, or include in the service layer only metadata properties tied to the UI (layering principle not respected).
3. Create specific validation attributes
Another alternative I was thinking of is to create specialized validation attributes. For example :
// In the service layer
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class MyValValidationAttribute : ValidationAttribute
{
public MyValValidationAttribute () : base() { }
public MyValValidationAttribute (Func<string> errorMessageAccessor) : base(errorMessageAccessor){}
public MyValValidationAttribute (string errorMessage) : base(errorMessage) {}
public override bool IsValid(object value)
{
var isValid = true;
var valueStr = value as string;
if (valueStr != null)
{
isValid = valueStr.Length <= 20;
}
return isValid;
}
}
public class MyCommand
{
[Required]
[MyValValidation]
public string MyVal { get; set;}
public string AnotherProperty { get; set;}
}
// In the web layer
public class MyCommandViewModel
{
[Required]
[MyValValidation]
public string MyVal { get; set;}
}
The pro of this method, is that I can centralize the validation of one rule in one place. The con is that I have to create nearly one attribute per validation rule.
Another possibility is maybe to use a 3rd party validation framework instead of the OOB validation framework, but I don't see the benefits (especially because asp.net mvc use this framework to create the validation of the form).
In layered applications, with a lot of business validation rules, what would be the best options ? I'm targeting the more evolutive possible application.
Thanks in advance for comments and suggestions.
PS: this question got his source in this other question