Is there a way to implement the idea of a data domains (at a property level) inside of a class that is used as a model in a view in ASP.Net MVC 4?
Consider this code:
public class LoginProfileModel {
[DisplayName("Login ID")]
[Required(ErrorMessage = "Login ID is required.")]
public string LogonID { get; set; }
[DisplayName("Password")]
[Required(ErrorMessage = "Password cannot be blank.")]
[StringLength(20, MinimumLength = 3)]
[DataType(DataType.Password)]
public string Password { get; set; }
}
Here is a LoginProfileModel in for ASP.Net MVC 4. It uses a variety of metadata/data annotations so that I can create a clean view with this code:
@model myWebSite.Areas.People.Models.LoginProfileModel
@using ( Html.BeginForm( "Index" , "Login" ) ) {
@Html.ValidationSummary()
@Html.EditorForModel()
<input type="submit" value="Login" />
}
I use the idea of a "Login ID" and a "Password" in more than one view, and therefore, in more than one view model. I want to be able to define the attributes that a Password uses, or possibly the Password itself with all of it's data annotations in a single location so that I can reuse all those definitions where needed, rather than respecifying them every time they are used:
[DisplayName("Password")]
[Required(ErrorMessage = "Password cannot be blank.")]
[StringLength(20, MinimumLength = 3)]
[DataType(DataType.Password)]
public string Password { get; set; }
Is that possible some way?
The following attributes affect the validation process of your View.
[Required(ErrorMessage = "Password cannot be blank.")]
[StringLength(20, MinimumLength = 3)]
For the Validation attributes, you can create a class like this:
public class PasswordRuleAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (new RequiredAttribute { ErrorMessage = "Password cannot be blank." }.IsValid(value) && new StringLengthAttribute(20) { MinimumLength=3 }.IsValid(value) )
return true;
return false;
}
}
You can use it as follows:
[PasswordRule]
public string Password{get;set;}
The other two attributes that you mentioned are Directly derived from the Attribute
class, and I don't think there's a way to consolidate them into a single attribute.
I'll update you with an edit soon.
So now we're left with:
[DisplayName("Password")]
[DataType(DataType.Password)]
[PasswordRule]
public string Password{get;set;}
EDIT:
According to this post: Composite Attribute, it's not possible to merge attributes.
You can do this using a buddy class, which provides metadata for your view model. Like so:
public partial class LogonMetaData
{
[DisplayName("Login ID")]
[Required(ErrorMessage = "Login ID is required.")]
public string LogonID { get; set; }
[DisplayName("Password")]
[Required(ErrorMessage = "Password cannot be blank.")]
[StringLength(20, MinimumLength = 3)]
[DataType(DataType.Password)]
public string Password { get; set; }
}
Then your view models:
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(LogonMetaData))]
public partial class FirstViewModel
{
public string LogonID { get; set; }
public string Password { get; set; }
}
using System.ComponentModel.DataAnnotations;
[MetadataType(typeof(LogonMetaData))]
public partial class SecondViewModel
{
public string LogonID { get; set; }
public string Password { get; set; }
}
Note the use of partial
in the class definitions. This is what allows this approach to work. One caveat, other than the obvious problem with DRY, is that I believe the metadata class has to reside in the same namespace as your view models, else it complains. Other than that, this should do what you want.
As a corollary to John H's answer, you could just use inheritance and make those view models that have the "idea of a LogonId and password" inherit from that base view model. This would solve the MetaData issues that were mentioned in the previous answer.
public class LoginProfileModel {
[DisplayName("Login ID")]
[Required(ErrorMessage = "Login ID is required.")]
public string LogonID { get; set; }
[DisplayName("Password")]
[Required(ErrorMessage = "Password cannot be blank.")]
[StringLength(20, MinimumLength = 3)]
[DataType(DataType.Password)]
public string Password { get; set; }
}
public SomeOtherClassThatNeedsLoginInfo : LoginProfileModel{
public string Property {get;set;}
}
Now in the SomeOtherClassThatNeedsLoginInfo, those properties and their related DataAnnotations will be available to you.
Another idea would be just to pass that LoginInfo as a property on your other view models.
public SomeOtherClassThatNeedsLoginInfo{
public string Property {get;set;}
public LoginProfileModel LoginModel {get;set;}
}