Jim Geurts
Jim Geurts

Reputation: 20429

How do I remain DRY with asp.net mvc view models & data annotation attributes?

How do I remain DRY with asp.net mvc view models & data annotation (validation, display, and data modeling) attributes with Asp.Net MVC? I have passed model objects as well as action specific view models to views. I find both directions to have some issues with trying to remain DRY.

How do I remain DRY with asp.net mvc view models & data annotation attributes?

Upvotes: 13

Views: 1863

Answers (3)

Jim Geurts
Jim Geurts

Reputation: 20429

So far, I've found that using inheritance to combine shared properties works best. I'm using a unique view class per action and am pretty happy with the solution so far. It doesn't solve 100% of the cases, but it does cover the majority and almost eliminates duplicate data contract attributes.

Upvotes: 0

CGK
CGK

Reputation: 2662

A good option would be to switch from DataAnnotations to Fluent Validation. It allows you to encapsulate common validation logic in a class, which you can apply later to your Models.

From the documentation:

[Validator(typeof(PersonValidator))]
public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

public class PersonValidator : AbstractValidator<Person> {
    public PersonValidator() {
        RuleFor(x => x.Id).NotNull();
        RuleFor(x => x.Name).Length(0, 10);
        RuleFor(x => x.Email).EmailAddress();
        RuleFor(x => x.Age).InclusiveBetween(18, 60);
    }
}

Upvotes: 5

UserControl
UserControl

Reputation: 15179

I have metadata defined in c# like this:

public class Meta
{
    public class Client
    {
        public class Name
        {
            public const bool Required = true;
            public const DataType Type = DataType.Text;
            public const int MaxLength = 30;
            public const int MinLength = 1;
            public const string Regex = @"^[\w\d\.-_]{1,30}$";
        }

        public class Email
        {
            public const bool Required = false;
            public const DataType Type = DataType.EmailAddress;
            public const int MaxLength = 256;
            public const int MinLength = 4;
            public const string Regex = @"^.+@.+$";
        }
    }
}

declaring them as constants allows you to utilize DataAnnotations on both BL entities and UI models:

[DataContract]
[Serializable]
public class ClientInfo
{
    [DataMember]
    [Required(AllowEmptyStrings = !Meta.Client.Name.Required)]
    [StringLength(Meta.Client.Name.MaxLength, MinimumLength = Meta.Client.Name.MinLength)]
    [RegularExpression(Meta.Client.Name.Regex)]
    public string Name { get; set; }

    ...
}

well, yes, you duplicate the attributes but not metadata! In addtition i have a trivial preprocessor to generate sql-scripts from a template (special processing for *.Required, etc):

create table dbo.Client( Name nvarchar({#Client.Name.MaxLength}) {#Client.Name.Required}, Email nvarchar({#Client.Email.MaxLength}) {#Client.Email.Required}, ....

On UI, you can use inheritance to not duplicate properties. For example, if you have a model with 10 properties, but need to edit only 2 of them, create EditModel and inherit ViewModel from it. The key here is to have metadata in a single storage and use it as much as possible. Hope you get the idea.

Upvotes: 3

Related Questions