Tuesday, November 30, 2010

Entity framework creating a Generic Repository and UnitOfWork implementation – Part 2

We have now moved our validation and base entity property change logic to the base repository implementation. So we can now remove the SavingChanges event code from the ObjectContext implementation. The changed code looks like
public class ShoppingCartContext : ObjectContext
{
    public ShoppingCartContext() : base("name=ShoppingCartModelContainer", "ShoppingCartModelContainer") { }

    public override int SaveChanges(SaveOptions options)
    {
        this.DetectChanges();
        return base.SaveChanges(options & ~SaveOptions.DetectChangesBeforeSave);
    }

    public ObjectSet<Customer> Customers
    {
        get { return _customers ?? CreateObjectSet<Customer>(); }
    }

    public ObjectSet<Order> Orders
    {
        get { return _orders ?? CreateObjectSet<Order>(); }
    }

    public ObjectSet<Product> Products
    {
        get { return _products ?? CreateObjectSet<Product>(); }
    }      

    ObjectSet<Customer> _customers;
    ObjectSet<Order> _orders;
    ObjectSet<Product> _products;
}

Once the base repository is ready, we can now create repositories for our entities in the model. I’ll show the code for the customer repository implementation.
public interface ICustomerRepository : IRepository<Customer>
{
    EF4Recipies.Models.Customer GetByEmail(string email);
    System.Collections.Generic.IEnumerableCustomer> SearchByName(string name);
}

public class CustomerRepository : BaseRepository<Customer>, ICustomerRepository
{
    public CustomerRepository(IUnitOfWork unitOfWork) : base(unitOfWork) { }

    public override IEnumerable<Customer> All()
    {
        return Context.Customers;
    }

    public override void Remove(Customer entity)
    {
        Context.Customers.Attach(entity);
        base.Remove(entity);
    }

    public override void Update(Customer entity)
    {
        Context.Customers.Attach(entity);
        base.Update(entity);
    }

    public Customer GetByEmail(string email)
    {
        return base.GetOneByCondition(customer => customer.Email == email);
    }

    public IEnumerable<Customer> SearchByName(string name)
    {
        return base.AllByCondition(customer => string.Concat(customer.FirstName, customer.LastName).Contains(name));
    }
}

Test cases:
[TestMethod()]
public void All_should_return_the_complete_set_of_customers_in_db()
{
    IUnitOfWork unitOfWork = UnitOfWork.Default;
    CustomerRepository target = new CustomerRepository(unitOfWork); 
           
    var customers = target.All();
    Assert.IsTrue(customers.Count() > 0);
}

[TestMethod()]
public void GetByEmail_should_return_customer_with_email_address_passed()
{
    IUnitOfWork unitOfWork = UnitOfWork.Default;
    CustomerRepository target = new CustomerRepository(unitOfWork);
    string email = "prajeesh.prathap@gmail.com";
    var customer = target.GetByEmail(email);
    Assert.AreEqual<string>(email, customer.Email);
}

[TestMethod]
public void Add_method_should_add_customer_to_the_context()
{
    IUnitOfWork unitOfWork = UnitOfWork.Default;
    CustomerRepository target = new CustomerRepository(unitOfWork);
    Customer customer = new Customer
    {
        FirstName = "SampleFName",
        LastName = "SampleLName",
        Email = "ValidEmail@gmail.com"               
    };

    target.Add(customer);
    unitOfWork.Save();

    unitOfWork = UnitOfWork.Default;
    CustomerRepository newRepository = new CustomerRepository(unitOfWork);
    var newCustomer = newRepository.GetByEmail("ValidEmail@gmail.com");
    Assert.IsNotNull(newCustomer);
    unitOfWork.ExecuteCommandOnDb("delete from [dbo].[Customers] where Id = '" + newCustomer.Id.ToString() + "'");           
}


Entity framework creating a Generic Repository and UnitOfWork implementation – Part 1

The repository and unit of work patterns have gained a lot of acknowledgement from the agile development community and have gained lot of popularity in the recent years. In these series of posts I’ll explain how to create a repository and unit of work implementation using entity framework POCO development. I’ll be using the models from the samples used in my previous posts on the topic. In you have missed those, I would recommend to check out these samples before looking into this.
My attempt is to create a base repository class which handles the GRUD operations and entity specific repository implementations have the additional functionality that are specific to business requirements and inherit this base class. The unit of work implementation is borrowed from the concept described in the post by Faisal Mohamood in the ADO.NET team blogs.
Implement Unit Of Work pattern using ObjectContext
Unit of work implementation have two methods Save() and ExecuteCommandOnDb.
The Save method calls the SaveChanges on the context. ExecuteCommandOnDb method is mainly used in the test cases where we need to directly execute a SQL query on the data store.
public class UnitOfWork : ShoppingCartContext, EF4Recipies.Repository.IUnitOfWork
{
    private UnitOfWork() { }

    public static IUnitOfWork Default
    {
        get
        {
            return new UnitOfWork();
        }
    }

    public void Save()
    {
        SaveChanges();
    }

    public void ExecuteCommandOnDb(string sqlCommand)
    {
        this.ExecuteStoreCommand(sqlCommand);
    }
}

Defining the interface for Repositories
The IRepository interface defines the common functionality that can be implemented in the base repository class. It looks like
public interface IRepository where T : BaseEntity
{
    void Add(T entity);
    System.Collections.Generic.IEnumerable All();
    System.Collections.Generic.IEnumerable AllByCondition(Funcbool> predicate);
    T GetById(Guid id);
    T GetOneByCondition(Funcbool> predicate);
    void Remove(T entity);
    void Update(T entity);
}

The base repository class implements this interface.
public abstract class BaseRepository : EF4Recipies.Repository.IRepository where T : BaseEntity
{
    public BaseRepository(IUnitOfWork unitOfwork)
    {
        if (unitOfwork == default(ShoppingCartContext)) throw new ArgumentNullException("UnitOfWork cannot be null");
        Context = unitOfwork as ShoppingCartContext;
    }

    public abstract IEnumerable All();

    public T GetById(Guid id)
    {
        return All().SingleOrDefault(entity => entity.Id == id);
    }

    public T GetOneByCondition(Funcbool> predicate)
    {
        return All().SingleOrDefault(predicate);
    }

    public IEnumerable AllByCondition(Funcbool> predicate)
    {
        return All().Where(predicate);
    }

    public void Add(T entity)
    {
        entity.Id = entity.Id == Guid.Empty ? Guid.NewGuid() : entity.Id;
        entity.CreatedDate = entity.ChangedDate = DateTime.Now;
        ValidateAndAddToObjectSet(entity);
    }

    private void ValidateAndAddToObjectSet(T entity)
    {
        entity.Validate();
        var objectSet = All() as ObjectSet;
        objectSet.Context.AddObject(objectSet.EntitySet.Name, entity);
    }

    public virtual void Remove(T entity)
    {
        Context.DeleteObject(entity);
    }

    public virtual void Update(T entity)
    {
        var entityFromDb = GetById(entity.Id);
        if (entityFromDb != null && StructuralComparisons.StructuralEqualityComparer.Equals(entity.Timestamp, entityFromDb.Timestamp))
        {
            entity.ChangedDate = DateTime.Now;
            ValidateAndAddToObjectSet(entity);
        }
        else
            throw new InvalidOperationException("Concurrency exception");
    }
       
    protected ShoppingCartContext Context { get; private set; }
}

The Add and Update methods calls the validation on the entities that we have implemented in this post.
The update method also checks for concurrency issues before updating by comparing the timestamp field value with the entity object to be saved. Both remove and Update methods are implemented as Virtual because these need to be attached to the context from the class that inherits the base class. We’ll see the implementation of a CustomerRepository using the base repository we implemented in the upcoming posts.

Monday, November 29, 2010

Entity Framework 4 Model first development with POCO – Implementing Entity Validations

In my previous post, I have demonstrated a sample with adding logic in the SavingChanges event on the ObjectContext class. The same event can be used for validation rules checking on the entities before the changes are applied to the data store. I have used the FluentValidation library from Jeremy Skinner for implementing the Validators on the entities. For e.g. validation rules for the Customer entity is implemented as.
public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
    {
        RuleFor<string>(c => c.Email)
            .EmailAddress().WithMessage("Invalid Email Address")
            .WithPropertyName("Email");

        RuleFor<string>(c => c.FirstName)
            .NotNull().WithMessage("Firstname cannot be null")
            .NotEmpty().WithMessage("Firstname cannot be empty")
            .Length(5, 50).WithMessage("Firstname should be in the range 5 - 50")
            .WithPropertyName("FirstName");

        RuleFor<string>(c => c.LastName)
            .NotNull().WithMessage("LastName cannot be null")
            .NotEmpty().WithMessage("LastName cannot be empty")
            .NotEqual(c => c.FirstName).WithMessage("Firstname and lastname cannot be equal")
            .Length(5, 50).WithMessage("LastName should be in the range 5 - 50")
            .WithPropertyName("LastName");
    }
}

The BaseEntity and other entities in the POCO implementation are now changed to have a new Validate method which calls the appropriate Validator and self validates them.
public abstract class BaseEntity
{
    public Guid Id { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime ChangedDate { get; set; }
    public byte[] Timestamp { get; set; }

    public abstract void Validate();
}

public class Customer : BaseEntity
{
    public Customer()
    {
        Orders = new HashSet<Order>();
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }

    public virtual ICollection<Order> Orders { get; set; }

    public override void Validate()
    {
        ValidationHelper.Validate<CustomerValidator, Customer>(this);
    }
}

And finally the ValidateEntities method to be called from the SavingChanges event
private void ValidateEntities()
{
    var validatableEntities = this.ObjectStateManager.GetObjectStateEntries
        (System.Data.EntityState.Added | System.Data.EntityState.Deleted | System.Data.EntityState.Modified)
        .Where(entity => entity.Entity is BaseEntity)
        .Select(entity => entity.Entity as BaseEntity);

    validatableEntities.ToList().ForEach(entity => entity.Validate());
}

Test Cases:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void ObjectContext_save_should_throw_exception_if_invalid_email_is_created_for_customer()
{
    var customer = new Customer
    {
        Email = "invalidEmail",
        FirstName = "Prajeesh",
        LastName = "Prathap"
    };

    using (var context = new ShoppingCartContext())
    {
        context.Customers.AddObject(customer);
        context.SaveChanges();
    }
}

The ValidationHelper class implementation for the sample
public class ValidationHelper
{
    public static void Validate(K entity)
        where T : IValidator, new()
        where K : BaseEntity
    {
        IValidator validator = new T();
        var validationResult = validator.Validate(entity);

        if (!validationResult.IsValid)
        {
            string validationError = GetError(validationResult);
            throw new InvalidOperationException(validationError);
        }
    }

    static string GetError(ValidationResult result)
    {
        var validationErrors = new StringBuilder();
        foreach (var validationFailure in result.Errors)
        {
            validationErrors.Append(validationFailure.ErrorMessage);
            validationErrors.Append(Environment.NewLine);
        }
        return validationErrors.ToString();
    }
}