Extend IQueryable instead of a certain dataprovider – more decoupled code

This is going to be a real short post and is more of an update to my last post (Entity validaton using Custom Data Annotation attributes) then a new one. I have made two small technical changes and with these small changes I have gained a more decoupled code.

Change One
Instead of having a helper method in my service base class I have put it in the entity store. This due to the fact that the validation is mere simple datavalidation and is pure validation of the entity’s state. Custom validation logic is still in my services but this logic is injected to the validation method in the entity store, via a Func<>.

Change Two
I don’t make use of repositories anymore. Instead I have a generic entity store that can handle whichever entity you pass to it, as long as you have provided mapping information about the entity. But what if I want to have “Named queries”, e.g GetAllUsersThatAreBlocked? In this case, I don’t create a custom implementation of my entity store by inheriting the generic implementation and creates the function “GetAllUsersThatAreBlocked”. No, I use my generic implementation that lets me get an IQueryable<T>, which I then extend. In my previous post I extended the entity store, which was really ugly. This new solution decouples my queries from the entity store and gives me the opportunity to execute the queries against any source that can provide me an IQueryable<T>.

The validation
The code below shows a Service-method and how it calls the ValidateEntity-method (which now is placed in the EntityStore).

public ServiceResponse<UserAccount> SetupNewUserAccount(UserAccount userAccount)
{
    var validationResult = EntityStore.ValidateEntity<UserAccount>(userAccount, CustomValidationForSettingUpNewAccount);
    var serviceResponse = new ServiceResponse<UserAccount>(userAccount, validationResult);
    
    if (!serviceResponse.ValidationResult.HasViolations)
    {
        EntityStore.AddEntity(userAccount);
        EntityStore.SaveChanges();
    }

    return serviceResponse;
}

private IEnumerable<ValidationResult> CustomValidationForSettingUpNewAccount(UserAccount userAccount)
{
    var violations = new List<ValidationResult>();
    var emailIsTaken = EntityStore.Query<UserAccount>().EmailIsTakenByOther(userAccount.Username, userAccount.Email);

    if (emailIsTaken)
        violations.Add(new ValidationResult("Email is allready taken."));

    return violations;
}

The ValidateEntityMethod is as before. Where it uses a EntityValidator that I have written about before. To perform custom validation you can pass in a Func<> which is done above, where I pass a pointer to method “CustomValidationForSettingUpNewAccount” in my service. This function only ensures that the email isn’t allready in use.

public 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);
}

Named queries as extensions to IQueryable<T>
The extension code is the same as in earliear post except that I now extend IQueryable instead of EfEntityStore and can hook on the where clause directly on the injected queryable. A verry little syntax change but a tremendous architechtural change since, the query extension now has no dependency to a certain implementation technique.

namespace Sds.Christmas.Storage.Queries.UserAccounts
{
    public static class UserAccountQueries
    {
        public static bool EmailIsTakenByOther(this IQueryable<UserAccount> userAccounts, string username, string email)
        {
            return
                userAccounts.Where(
                    u =>
                        u.Username.Equals(username, StringComparison.InvariantCultureIgnoreCase) &&
                        u.Email.Equals(email, StringComparison.InvariantCultureIgnoreCase)).Count() > 0;
        }
    }
}

As always, there’s a complete sample project available for download.

Enjoy!

//Daniel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s