Translate

Saturday, 4 October 2014

The Repository Pattern - I (The Interface and ConcreteRepository)

The use case for a repository can be many - code, books, project etc but it is when these repos need to be combined and managed that the Repository pattern begins to make sense and the SharpRepository and the Unit of work technique to maintain/manage transactions for the different repositories, gain importance.

How the aggregate is defined and managed is how the question of whether 'one size fits all?' question will get an answer.

Although there is nothing new that could probably be added to the many excellent resources on the SharpRepository implementation, I did find some curious elements that were not as well or as clearly implemented in the many available resources on the web. This is the simplest of them all.

I will post my code first and continue with the explanations inline as and when and where I found it necessary.

The example that I have chosen is dependent on many interfaces and concrete implementations so posting the fully tested code will be done in stages.

The interface

using System.Collections.Generic;

namespace LinqExpressionsHashSet
{
    interface IRepository<T>
    {
            T GetById(int id);
            T GetByName(string name);
            IEnumerable<T> GetAll();
            IEnumerable<T> FindAll(IDictionary<string, object> propertyValuePairs);
            T FindOne(IDictionary<string, object> propertyValuePairs);
            T SaveOrUpdate(T entity);
            void Delete(T entity);  
    }
}

The concrete repository

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using SharpRepository.Repository.Queries;

namespace LinqExpressionsHashSet
{
        public class Repository<T> : IRepository<T>,IEnumerable<T> where T : class,IIdentifiable,new()
        {
          // for InMemoryRepository testing
            private readonly HashSet<T> data;
            private readonly IQueryable<T> queryableData;

        public Repository()
            : this(Enumerable.Empty<T>())
        {
        }
        public Repository(IEnumerable<T> testData)
        {
            data = new HashSet<T>(testData);
            queryableData = data.AsQueryable();
        }

        public Expression Expression { get; private set; }

        public Type ElementType { get; set; }

        public IQueryProvider Provider { get; private set; }

        public void Add(T entity)
        {
            data.Add(entity);
        }

        public void Add(IEnumerable<T> entities)
        {
            foreach (var e in entities)
            {
                data.Add(e);
            }
        }

        public void Update(T entity)
        {
            data.RemoveWhere(i => i.Id == entity.Id);
            data.Add(entity);
        }

        public void Update(IEnumerable<T> entities)
        {
            foreach (var e in entities)
            {
                Update(e);
            }
        }
        /* To use for Unit of Work
        public IBatch<T> BeginBatch()
        {
            return (IBatch<T>)data.ToList<T>();
        }
        */
        public void Delete(T entity)
        {
            data.RemoveWhere(i => i.Id == entity.Id);
        }

        public void Delete(IEnumerable<T> entities)
        {
            foreach (var e in entities)
            {
                data.RemoveWhere(x => x.Id == e.Id); // The id is from MongoDB
            }
        }

        public T Find(Expression<Func<T, bool>> predicate, IQueryOptions<T> queryOptions = null)
        {
//            Extracting the lambda expression
            Expression<Func<T, bool>> ep = Expression.Lambda<Func<T, bool>>(predicate);
            // without queryOptions
            return queryableData.First(ep);
        }

/*
            //Need to test this
        public TResult Find<TResult>(Expression<Func<T, bool>> predicate, Expression<Func<T, TResult>> selector,
                                     IQueryOptions<T> queryOptions = null)
        {
            Expression<Func<T, TResult>> ep = Expression.Lambda<Func<T, TResult>>(selector);
            IEnumerable<TResult> someResults = queryableData.Select(selector);
            Expression<Func<T, bool>> exp = Expression.Lambda<Func<T, bool>>(predicate);
            TResult tt = default(TResult); // to escape uninitialized compiler error and null.
            foreach (TResult a in someResults)
            {
                var e = queryableData.First(exp);
                if (data.Contains(e))
                {
                    tt = a;
                    break;
                }
            }
            return tt;
        }
*/
        public bool Exists(Expression<Func<T, bool>> predicate)
        {
            Expression<Func<T, bool>> ep = Expression.Lambda<Func<T, bool>>(predicate);
            Func<T, bool> fexp = ep.Compile();
            var t = fexp(new T()); // Returns bool from the expression

            return t; // This is not the correct return value. Used only to pass the compiler.
        }

            /* To be used with EF
            protected DbSet<T> DbSet;

            public Repository(DbContext dataContext)
            {
                DbSet = dataContext.Set<T>();
            }
         
            public void Insert(T entity)
            {
                DbSet.Add(entity);
            }

            public void Delete(T entity)
            {
                DbSet.Remove(entity);
            }

            public IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate)
            {
                return DbSet.Where(predicate);
            }

            public IQueryable<T> GetAll()
            {
                return DbSet;
            }

            public T GetById(int id)
            {
                return DbSet.Find(id);
            }
             */
        }
    }

and an identifiable interface for in memory and MongoDB implementation. This could be substituted with an IEntity interface that can help provide the 'id' but on this (the BS, that is :)) I will post later. This is being used to query an expression with an 'id' in the Delete method above.

// The Identity interface

using MongoDB.Bson;
namespace LinqExpressionsHashSet
{
    public interface IIdentifiable : IIdentifiable<ObjectId>
    {
        string StringId { get; }
    }
    public interface IIdentifiable<T>
    {
        T Id { get; }
    }

}

// The Project repo
using System;

namespace LinqExpressionsHashSet
{
    public class Project:IIdentifiable
    {
        public int projectId { get; set; }
        public string name{get;set;}
        public DateTime createdOn { get; set; }
        public string createdBy { get; set; }
    }
}

More to follow...:)

No comments: