Entity validaton using Custom Data Annotation attributes

Ok. Today I have extended the validation part a bit. The code is about 90% the same from the post I wrote yesterday (Entity framework 4 – Part 5 – Validation using Data Annotations). But I have now added some custom data annotation attributes and made it possible for the services to add custom validation of the entities.

I will not show all the code since much of it is covered in Entity framework 4 – Part 5 – Validation using Data Annotations and as always, you can download a complete code example.

The EntityValidator
It uses the builtin Validator and ValidationResult found in System.ComponentModel.DataAnnotations. It validates sent entities by looking at your entity for validation attributes. I have now also made it possible to inject a Func, which lets you perform custom validation. What I medan by this is that you can provide a func that generates validation results that gets merged into the validation results generated by the validation attributes. The Func takes the Entity and a bool as in-params. The bool contains true if the validation attributes resulted in a valid entity. The result of the Func should be an IEnumerable, which will be merged by the validationresults generated by the validation attributes.

public class EntityValidator<T> where T : IEntity
{
    public EntityValidationResult Validate(T entity, Func<T, bool, IEnumerable<ValidationResult>> customValidation = null)
    {
        var validationResults = new List<ValidationResult>();
        var vc = new ValidationContext(entity, null, null);
        var isValid = Validator.TryValidateObject(entity, vc, validationResults, true);

        if (customValidation != null)
            validationResults.AddRange(customValidation(entity, isValid));

        return new EntityValidationResult(validationResults);
    }
}

The Service
To ease things in my services I have implemented a helper method in a base-class which my services extends. This method will only invoke the customvalidation Func if the validation attributes haven’t generated an invalid entity.

public abstract class Service
{
    protected IEntityStore EntityStore { get; private set; }

    protected Service(IEntityStore entityStore)
    {
        EntityStore = entityStore;
    }

    protected EntityValidationResult ValidateEntity<T>(T entity, Func<T, IEnumerable<ValidationResult>> customValidation = null)
        where T : IEntity
    {
        Func<T, bool, IEnumerable<ValidationResult>> customValidationProxy = null;
        
        if (customValidation != null)
            customValidationProxy = (e, isValid) => isValid ? customValidation(e) : null;

        return new EntityValidator<T>().Validate(entity, customValidationProxy);
    }
}

In my Security-service I provide some custom validation to check if the Email isn’t allready taken, by providing a customvalidation Func. I also make use of some extension methods to get access to named queries for my UserAccount entities.

public class SecurityService : Service, ISecurityService
{
    public SecurityService(IEntityStore entityStore)
        : base(entityStore)
    {
    }

    public ServiceResponse<UserAccount> SetupNewUserAccount(UserAccount userAccount)
    {
        var validationResult = ValidateEntity<UserAccount>(userAccount, CustomValidationForSettingUpNewAccount);

        if (!validationResult.HasViolations)
        {
            EntityStore.AddEntity(userAccount);
            EntityStore.SaveChanges();
        }

        return new ServiceResponse<UserAccount>(userAccount, validationResult);
    }

    private IEnumerable<ValidationResult> CustomValidationForSettingUpNewAccount(UserAccount userAccount)
    {
        var violations = new List<ValidationResult>();

        var emailIsTaken = EntityStore.EmailIsTakenByOther(userAccount.Username, userAccount.Email);
        if (emailIsTaken)
            violations.Add(new ValidationResult("Email is allready taken."));

        return violations;
    }
}

Named queries – Extension methods to my Entitystore
Since I don’t use a custom implementaion of a Entitystore for the application (although there is one provided in the example code), I have implemented my specific entity queries as extension methods. So if I want access to e.g. specific queries for my useraccounts, I just import the namespace where they are located (Sds.Christmas.Storage.Queries.UserAccounts).

using Sds.Christmas.Storage.Queries.UserAccounts;

public static class UserAccountQueries
{
    public static bool EmailIsTakenByOther(this IEntityStore entityStore, string username, string email)
    {
        return
            entityStore.Query<UserAccount>().Where(
                u =>
                    u.Username.Equals(username, StringComparison.InvariantCultureIgnoreCase) &&
                    u.Email.Equals(email, StringComparison.InvariantCultureIgnoreCase)).Count() > 0;
    }
}

Custom data annotaions
Since my UserAccount contains an Email I have created a custom Email-validationattribute and applied it to the Email-property. The errormessages are retrieved via resx-files.

[Serializable]
public class UserAccount
    : Entity
{
    [Required(AllowEmptyStrings = false, ErrorMessageResourceName = "UserAccountRequiredUsername", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    [StringRange(MinLength=5, MaxLength=20, ErrorMessageResourceName = "UserAccountInvalidLength", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    public virtual string Username { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceName = "UserAccountRequiredPassword", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    public virtual string Password { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceName = "UserAccountRequiredEmail", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    [Email(ErrorMessageResourceName = "UserAccountEmailHasInvalidFormat", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    public virtual string Email { get; set; }
}

[Serializable]
public class EmailAttribute : RegexAttribute
{
    public EmailAttribute() : base(@"^[\w-\.]{1,}\@([\w]{1,}\.){1,}[a-z]{2,4}$", RegexOptions.IgnoreCase)
    {}
}

[Serializable]
public class RegexAttribute : ValidationAttribute
{
    public string Pattern { get; set; }
    public RegexOptions Options { get; set; }

    public RegexAttribute(string pattern, RegexOptions options = RegexOptions.None)
    {
        Pattern = pattern;
        Options = options;
    }

    public override bool IsValid(object value)
    {
        return IsValid(value as string);
    }

    public bool IsValid(string value)
    {
        return string.IsNullOrEmpty(value) ? true : new Regex(Pattern, Options).IsMatch(value);
    }
}

If you now try to add two different useraccounts with the same Email, you will not succed.

//Setup new useraccount using Service
var userAccount = SetupNewUserAccount();
var userAccount2 = SetupNewUserAccount(); //=> Gives Email is allready taken error.

That’s it. Don’t forget to download and explore the code.

//Daniel

Entity framework 4 – Part 5 – Validation using Data Annotations

This post is part of a series of post where I cover different aspects of the new version of Entity framework and the CTP features. I do focus on creating a POCO solutions, hence I don’t use the designer to generate my model.

Part 1 – Getting started
Part 2 – Relationships between non public members
Part 3 – Adding pluralization support to a non strongly typed ObjectContext.
Part 4 – Autoregister Entitymappings

Download the code.

This time I will look at validation using the “System.ComponentModel.DataAnnotations” lib. NOTE! That it doesn’t say “System.Data.Entity.DataAnnotations” or any other specific part of the .Net framework. It is a common lib that could be used in e.g ASP.Net MVC.

Specify the rules
Lets start with the rules. I have chosen to decorate my entities with attributes to specify the rules for them. This is as easy as:

1. Add reference to “System.ComponentModel.DataAnnotations”
2. Import namespace
3. Start decorating your classes.

This looks like:

[Serializable]
public class UserAccount
    : Entity
{
    [Required(AllowEmptyStrings = false, ErrorMessageResourceName = "UserAccountRequiredUsername", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    public virtual string Username { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceName = "UserAccountRequiredPassword", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    public virtual string Password { get; set; }

    [Required(AllowEmptyStrings = false, ErrorMessageResourceName = "UserAccountRequiredEmail", ErrorMessageResourceType = typeof(ModelValidationMessages))]
    public virtual string Email { get; set; }
}

I have not created any custom attribute to take care of specific validation like an “EmailAttribute”, but it can be done. I have chosen to go with resources, but you can go with simple errormessages which would look like:

[Required(AllowEmptyStrings = false, ErrorMessage="You must provide an username.")]
public virtual string Username { get; set; }

Validate an entity
In this example I’m performing the validation in my services. This is something that isn’t necessary. E.g if you are using the new MVC 2 the default modelbinder can handle the validation for you.

To fulfill “DRY” (Dont repeat yourself) I have created a base class for my services, which contains a helper-method that validates entities. The method is “ValidateEntity”. It returns an instance of my EntityValidationResult-class which contains the violations. I’m using this instance to determine if the entity should be persisted or not. The violations are passed back to the serviceconsumer via a ServiceResponse, so I don’t throw any ugly business exceptions.

public class SecurityService : Service, ISecurityService
{
    public SecurityService(IEntityStore entityStore)
        : base(entityStore)
    {
    }

    public ServiceResponse<UserAccount> SetupNewUserAccount(UserAccount userAccount)
    {
        var validationResult = ValidateEntity<UserAccount>(userAccount);

        if (!validationResult.HasViolations)
        {
            EntityStore.AddEntity(userAccount);
            EntityStore.SaveChanges();
        }

        return new ServiceResponse<UserAccount>(userAccount, validationResult);
    }
}

The ValidateEntity method is really simple, since it just consumes another object, my EntityValidator. The code for this looks like:

In the baseclass: “Service”:

protected EntityValidationResult ValidateEntity<T>(T entity)
    where T : IEntity
{
    return new EntityValidator<T>().Validate(entity);
}
public class EntityValidator<T> where T : IEntity
{
    public EntityValidationResult Validate(T entity)
    {
        var validationResults = new List<ValidationResult>();
        var vc = new ValidationContext(entity, null, null);
        var isValid = Validator.TryValidateObject(entity, vc, validationResults);

        return new EntityValidationResult(validationResults);
    }
}

The EntityValidator just consumes classes found in the System.ComponentModel.DataAnnotations library and returns eventual violations by wrapping them in my EntityValidationResult-class.

[Serializable]
public class EntityValidationResult
{
    public IList<ValidationResult> Violations { get; private set; }
    public bool HasViolations
    {
        get { return Violations.Count > 0; }
    }

    public EntityValidationResult(IList<ValidationResult> violations = null)
    {
        Violations = violations ?? new List<ValidationResult>();
    }
}

Handle the violations
Since I’m just fiddling a bit I have a simple consoleapplication which only outputs the errors:

private static void IfFailedServiceOperationInformUser<T>(ServiceResponse<T> serviceResponse)
{
    if (serviceResponse.Status == ServiceResponseStatuses.Succeeded)
        return;

    Console.WriteLine("Service failed!");

    if (serviceResponse.ValidationResult.HasViolations)
    {
        Console.WriteLine("Violations are:");
        foreach (var violation in serviceResponse.ValidationResult.Violations)
        {
            foreach (var member in violation.MemberNames)
                Console.WriteLine("\t+" + member);

            Console.WriteLine("\t" + violation.ErrorMessage);
        }
    }
}

Thats it for now. As always you can download a complete example, which has evolved since part 1 of this post-series.

//Daniel