Click here to Skip to main content
Click here to Skip to main content

Implementing Repository Pattern With Entity Framework

By , 2 Nov 2012
 

Introduction  

 

The Repository pattern is defined by Patterns of Enterprise Application Architecture as:   

Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. 

Repository provides an in-memory like collection interface for accessing domain objects. So as far as the consuming component is concerned, it uses the repository just like a collection when working with Domain objects. The repository then neatly abstracts the internal mechanics of how the Add / Remove calls to the repository translate to the actual data access calls to the data store. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers.

So with the repository we get a nice abstraction that provides us with persistence ignorance and a nice separation of concerns where the responsibility of persisting domain objects is encapsulated by the Repository leaving the domain objects to deal entirely with the domain model and domain logic.Here are some reasons for using repository pattern in data access layer in place direct access the database code-

  • Duplicated code 
  • A higher potential for programming errors
  • Weak typing of the business data
  • Difficulty in centralizing data-related policies such as caching
  • An inability to easily test the business logic in isolation from external dependencies 

Using the Code 

Here I use the Composite Repository pattern:

    /// <summary>
    /// Repository Interface defines the base
    /// functionality required by all Repositories.
    /// </summary>
    /// <typeparam name="T">
    /// The entity type that requires a Repository.
    /// </typeparam>
    public interface IRepository<E>
    {
        string KeyProperty { get; set; }

        void Add(E entity);
        void AddOrAttach(E entity);
        void DeleteRelatedEntries(E entity);
        void DeleteRelatedEntries
        (E entity, ObservableCollection<string> keyListOfIgnoreEntites);
        void Delete(E entity);

        ObjectQuery<E> DoQuery();
        ObjectQuery<E> DoQuery(ISpecification<E> where);
        ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex);
        ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression);
        ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression,
                    int maximumRows, int startRowIndex);

        IList<E> SelectAll(string entitySetName);
        IList<E> SelectAll();
        IList<E> SelectAll(string entitySetName, ISpecification<E> where);
        IList<E> SelectAll(ISpecification<E> where);
        IList<E> SelectAll(int maximumRows, int startRowIndex);
        IList<E> SelectAll(Expression<Func<E, object>> sortExpression);
        IList<E> SelectAll(Expression<Func<E, object>> sortExpression,
                    int maximumRows, int startRowIndex);

        E SelectByKey(string Key);

        bool TrySameValueExist(string fieldName, object fieldValue, string key);
        bool TryEntity(ISpecification<E> selectSpec);

        int GetCount();
        int GetCount(ISpecification<E> selectSpec);
    }

 

You can write your own Repository for each business object like RoleRepository, UserReporsitory, etc. Or you can implement this interface as a generic class of Repository something like this:   

    public class Repository<E, C> : IRepository<E>
        where E : class
        where C : ObjectContext
    {
        private readonly C _ctx;

        private string _KeyProperty = "ID";

        public string KeyProperty
        {
            get
            {
                return _KeyProperty;
            }
            set
            {
                _KeyProperty = value;
            }
        }

        public C Session
        {
            get { return _ctx; }
        }

        public Repository(C session)
        {
            _ctx = session;
        }

        #region IRepository<E,C> Members

        public int Save()
        {
            return _ctx.SaveChanges();
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        private ObjectQuery<E> DoQuery(string entitySetName)
        {
            return _ctx.CreateQuery<E>("[" + entitySetName + "]");
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public ObjectQuery<E> DoQuery()
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name) + "]");
        }

        /// <summary>
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        private ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
        {
            return
                (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
                .Where(where.EvalPredicate);
        }

        /// <summary>
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public ObjectQuery<E> DoQuery(ISpecification<E> where)
        {
            return
                (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]")
                .Where(where.EvalPredicate);
        }
        /// <summary>
        /// Query Entity with Paging 
        /// </summary>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection of Entities</returns>
        public ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex)
        {
            return (ObjectQuery<E>)_ctx.CreateQuery<E>
        ("[" + this.GetEntitySetName(typeof(E).Name) + "]").Skip<E>(startRowIndex).Take(maximumRows);
        }
        /// <summary>
        /// Query Entity in sorted Order
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="ErrorCode">custom Error Message</param> 
        /// <returns>Collection of Entities</returns>
        public ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression)
        {
            if (null == sortExpression)
            {
                return this.DoQuery();
            }
            return (ObjectQuery<E>)((IRepository<E>)this).DoQuery().OrderBy
                        <E, object>(sortExpression);
        }
        /// <summary>
        /// Query All Entity in sorted Order with Paging support
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection Of entities</returns>
        public ObjectQuery<E> DoQuery(Expression<Func<E, object>>
            sortExpression, int maximumRows, int startRowIndex)
        {
            if (sortExpression == null)
            {
                return ((IRepository<E>)this).DoQuery(maximumRows, startRowIndex);
            }
            return (ObjectQuery<E>)((IRepository<E>)this).DoQuery
            (sortExpression).Skip<E>(startRowIndex).Take(maximumRows);
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll(string entitySetName)
        {
            return DoQuery(entitySetName).ToList();
        }
        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll()
        {
            try
            {
                return DoQuery().ToList(); //_ctx.CreateQuery<E>("[" + typeof(E).Name + "]");
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <param name=”entitySetName”>
        /// The EntitySet name of the entity in the model.
        /// </param>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll(string entitySetName, ISpecification<E> where)
        {
            return DoQuery(entitySetName, where).ToList();
        }

        /// <summary>
        /// A generic method to return ALL the entities
        /// </summary>
        /// <typeparam name=”TEntity”>
        /// The Entity to load from the database.
        /// </typeparam>
        /// <returns>Returns a set of TEntity.</returns>
        public IList<E> SelectAll(ISpecification<E> where)
        {
            return DoQuery(where).ToList();
        }
        /// <summary>
        /// Select All Entity with Paging 
        /// </summary>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection of Entities</returns>
        public IList<E> SelectAll(int maximumRows, int startRowIndex)
        {
            return DoQuery(maximumRows, startRowIndex).ToList();
        }
        /// <summary>
        /// Select All Entity in sorted Order
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="ErrorCode">custom Error Message</param> 
        /// <returns>Collection of Entities</returns>
        public IList<E> SelectAll(Expression<Func<E, object>> sortExpression)
        {
            if (null == sortExpression)
            {
                return DoQuery(sortExpression).ToList();
            }
            return DoQuery(sortExpression).ToList();
        }
        /// <summary>
        /// Select All Entity in sorted Order with Paging support
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection Of entities</returns>
        public IList<E> SelectAll(Expression<Func<E, object>>
            sortExpression, int maximumRows, int startRowIndex)
        {
            if (sortExpression == null)
            {
                return DoQuery(maximumRows, startRowIndex).ToList();
            }
            return DoQuery(sortExpression, maximumRows, startRowIndex).ToList();
        }
        /// <summary>
        /// Get Entity By Primary Key
        /// </summary>
        /// <typeparam name="E">Entity Type</typeparam>
        /// <param name="Key">Primary Key Value</param>
        /// <returns>return entity</returns>
        public E SelectByKey(string Key)
        {
            // First we define the parameter that we are going to use the clause. 
            var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
            MemberExpression leftExpr = MemberExpression.Property(xParam, this._KeyProperty);
            Expression rightExpr = Expression.Constant(Key);
            BinaryExpression binaryExpr = MemberExpression.Equal(leftExpr, rightExpr);
            //Create Lambda Expression for the selection 
            Expression<Func<E, bool>> lambdaExpr =
            Expression.Lambda<Func<E, bool>>(binaryExpr,
            new ParameterExpression[] { xParam });
            //Searching ....
            var resultCollection = (ObjectQuery<E>)_ctx.CreateQuery<E>("[" + this.GetEntitySetName(typeof(E).Name) + "]")
                .Where(lambdaExpr);
            if (null != resultCollection && resultCollection.Count() > 0)
            {
                //return valid single result
                return resultCollection.First<E>();
            }//end if 
            return null;
        }
        /// <summary>
        /// Check if value of specific field is already exist
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="fieldName">name of the Field</param>
        /// <param name="fieldValue">Field value</param>
        /// <param name="key">Primary key value</param>
        /// <returns>True or False</returns>
        public bool TrySameValueExist(string fieldName, object fieldValue, string key)
        {
            // First we define the parameter that we are going to use the clause. 
            var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
            MemberExpression leftExprFieldCheck =
            MemberExpression.Property(xParam, fieldName);
            Expression rightExprFieldCheck = Expression.Constant(fieldValue);
            BinaryExpression binaryExprFieldCheck =
            MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);

            MemberExpression leftExprKeyCheck =
            MemberExpression.Property(xParam, this._KeyProperty);
            Expression rightExprKeyCheck = Expression.Constant(key);
            BinaryExpression binaryExprKeyCheck =
            MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
            BinaryExpression finalBinaryExpr =
            Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);

            //Create Lambda Expression for the selection 
            Expression<Func<E, bool>> lambdaExpr =
            Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
            new ParameterExpression[] { xParam });
            //Searching ....            
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName(typeof(E).Name) + "]").Any<E>
                        (lambdaExpr);
        }
        /// <summary>
        /// Check if Entities exist with Condition
        /// </summary>
        /// <param name="selectExpression">Selection Condition</param>
        /// <returns>True or False</returns>
        public bool TryEntity(ISpecification<E> selectSpec)
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]").Any<E>
                        (selectSpec.EvalPredicate);
        }
        /// <summary>
        /// Get Count of all records
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <returns>count of all records</returns>
        public int GetCount()
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]").Count();
        }
        /// <summary>
        /// Get count of selection
        /// </summary>
        /// <typeparam name="E">Selection Condition</typeparam>
        /// <returns>count of selection</returns>
        public int GetCount(ISpecification<E> selectSpec)
        {
            return _ctx.CreateQuery<E>("[" + this.GetEntitySetName( typeof(E).Name ) + "]")
                .Where(selectSpec.EvalPredicate).Count();
        }
        /// <summary>
        /// Delete data from context
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="entity"></param>
        public void Delete(E entity)
        {
            _ctx.DeleteObject(entity);
        }
        /// <summary>
        /// Delete data from context
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="entity"></param>
        public void Delete(object entity)
        {
            _ctx.DeleteObject(entity);
        }
        /// <summary>
        /// Insert new data into context
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="entity"></param>
        public void Add(E entity)
        {
            _ctx.AddObject( this.GetEntitySetName(entity.GetType().Name), entity);
        }
        /// <summary>
        /// Insert if new otherwise attach data into context
        /// </summary>
        /// <param name="entity"></param>
        public void AddOrAttach(E entity)
        {
            // Define an ObjectStateEntry and EntityKey for the current object.
            EntityKey key;
            object originalItem;
            // Get the detached object's entity key.
            if (((IEntityWithKey)entity).EntityKey == null)
            {
                // Get the entity key of the updated object.
                key = _ctx.CreateEntityKey(this.GetEntitySetName(entity.GetType().Name), entity);
            }
            else
            {
                key = ((IEntityWithKey)entity).EntityKey;
            }
            try
            {
                // Get the original item based on the entity key from the context
                // or from the database.
                if (_ctx.TryGetObjectByKey(key, out originalItem))
                {//accept the changed property
                    if (originalItem is EntityObject &&
                        ((EntityObject)originalItem).EntityState != EntityState.Added)
                    {
                        // Call the ApplyCurrentValues method to apply changes
                        // from the updated item to the original version.
                        _ctx.ApplyCurrentValues(key.EntitySetName, entity);
                    }
                }
                else
                {//add the new entity
                    Add(entity);
                }//end else
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// Delete all related entries
        /// </summary>
        /// <param name="entity"></param>        
        public void DeleteRelatedEntries(E entity)
        {
            foreach (var relatedEntity in (((IEntityWithRelationships)entity).
        RelationshipManager.GetAllRelatedEnds().SelectMany(re =>
        re.CreateSourceQuery().OfType<EntityObject>()).Distinct()).ToArray())
            {
                _ctx.DeleteObject(relatedEntity);
            }//end foreach
        }
        /// <summary>
        /// Delete all related entries
        /// </summary>
        /// <param name="entity"></param>        
        public void DeleteRelatedEntries(E entity, ObservableCollection<string>
                            keyListOfIgnoreEntites)
        {
            foreach (var relatedEntity in (((IEntityWithRelationships)entity).
            RelationshipManager.GetAllRelatedEnds().SelectMany(re =>
            re.CreateSourceQuery().OfType<EntityObject>()).Distinct()).ToArray())
            {
                PropertyInfo propInfo = relatedEntity.GetType().GetProperty
                            (this._KeyProperty);
                if (null != propInfo)
                {
                    string value = (string)propInfo.GetValue(relatedEntity, null);
                    if (!string.IsNullOrEmpty(value) &&
                        keyListOfIgnoreEntites.Contains(value))
                    {
                        continue;
                    }//end if 
                }//end if
                _ctx.DeleteObject(relatedEntity);
            }//end foreach
        }

        private string GetEntitySetName(string entityTypeName)
        {
            var container = this._ctx.MetadataWorkspace.GetEntityContainer
                    (this._ctx.DefaultContainerName, DataSpace.CSpace);

            return (from meta in container.BaseEntitySets

                                    where meta.ElementType.Name == entityTypeName

                                    select meta.Name).FirstOrDefault(); 
                     
        }

        #endregion
    }

Notice that, here I also implement IDispose interface to dispose the context manually.To get the name of Entityset, here I have used typeof, but you can do a  metadata query to retrieve the EntitySet name:

container = context.MetadataWorkspace.GetEntityContainer
		(context.DefaultContainerName, DataSpace.CSpace); 

string entitySetName = (from meta in container.BaseEntitySets

                            where meta.ElementType.Name == entityTypeName

                            select meta.Name).FirstOrDefault();   

Here I am not going a little into the code. I have used ObjectQuery to get the features of MargeOption and EnablePlanCaching properties: 

ObjectQuery.EnablePlanCaching Property -indicates whether the query plan should be cached. Plan-caching caches information which is computed as part of putting together the query itself. By caching this, a subsequent execution of the same query (even if you change parameter values) will run much faster than the first one. This information is cached per app-domain so you will generally benefit from the query cache across multiple client requests to the same web app and the like. Here all DoQuery methods are responsible to queries and other query method like SelectAll or Select methods internally use these DoQuery methods with various parameters. 

Setting MergeOption to NoTracking has helped us to take advantage of the EF ability to return data that doesn’t need to be tracked by the context. Say I don’t have any plan to make changes to that data. Therefore, I like to avoid the performance hit taken when EF creates ObjectStateEntry instances for each object it’s tracking, as well as forcing the context to be aware of any changes made to those objects. 

SelectByKey is creating a LINQ expression using Expression tree for Primary key and During creating the repository on an entity you can supply this Primary Key property as string. While using the POCO as you entity you can set attribute programming to serve this kind of job.

TrySameValueExist is doing the same job by allow you to set customize field and value comparison there. What will do is create the Expression for you and also add the PrimaryKey comparison so that it excludes the object that you are querying for (and where PrimaryKey != currentObjectPrimaryKey).

Add and Delete method simply respectively call the AddObject and DeleteObject Method of ObjectContextAddOrAttach method is for special situation where you don’t know whether object is already added or not. It is expensive since it will do query to database to check the existence. 

The specification pattern can implement a re-usable business logic component that can be passed around to satisfy certain business criteria. The specification object has a clear and limited responsibility, which can be separated and decoupled from the domain object that uses it. I would highly recommend reading the white paper by Martin Fowler and Eric Evans on the Specification pattern.   

public interface ISpecification<E>
{
    /// <summary>
    /// Select/Where Expression
    /// </summary>
    Expression<Func<E, bool>> EvalPredicate { get; }
    /// <summary>
    /// Function to evaluate where Expression
    /// </summary>
    Func<E, bool> EvalFunc { get; }
}

You can write your own specification by implementing your interface like RoleSpecification and put down your business logic there. For general use, you can also implement the Interface; such composite specification like this:

    public class Specification<E> : ISpecification<E>
    {
        #region Private Members

        private Func<E, bool> _evalFunc = null;
        private Expression<Func<E, bool>> _evalPredicate;

        #endregion

        #region Virtual Accessors

        public virtual bool Matches(E entity)
        {
            return _evalPredicate.Compile().Invoke(entity);
        }

        public virtual Expression<Func<E, bool>> EvalPredicate
        {
            get { return _evalPredicate; }
        }

        public virtual Func<E, bool> EvalFunc
        {
            get { return _evalPredicate != null ? _evalPredicate.Compile() : null; }
        }

        #endregion

        #region Constructors

        public Specification(Expression<Func<E, bool>> predicate)
        {
            _evalPredicate = predicate;
        }

        private Specification() { }

        #endregion

        #region Private Nested Classes

        private class AndSpecification : Specification<E>
        {
            private readonly ISpecification<E> _left;
            private readonly ISpecification<E> _right;
            public AndSpecification(ISpecification<E> left, ISpecification<E> right)
            {
                this._left = left;
                this._right = right;

                this._evalFunc =
                    (Func<E, bool>)Delegate.Combine
                    (left.EvalPredicate.Compile(),
                    right.EvalPredicate.Compile());

                _evalPredicate = left.EvalPredicate.And(right.EvalPredicate);
            }
            public override bool Matches(E entity)
            {
                return EvalPredicate.Compile().Invoke(entity);
            }
        }

        private class OrSpecification : Specification<E>
        {
            private readonly ISpecification<E> _left;
            private readonly ISpecification<E> _right;
            public OrSpecification(ISpecification<E> left, ISpecification<E> right)
            {
                this._left = left;
                this._right = right;

                this._evalFunc =
                    (Func<E, bool>)Delegate.Combine
                    (left.EvalPredicate.Compile(),
                    right.EvalPredicate.Compile());

                _evalPredicate = left.EvalPredicate.Or(right.EvalPredicate);
            }
            public override bool Matches(E entity)
            {
                return EvalPredicate.Compile().Invoke(entity);
            }
        }

        #endregion

        #region Operator Overloads

        public static Specification<E> operator &(Specification<E> left, ISpecification<E> right)
        {
            return new AndSpecification(left, right);
        }

        public static Specification<E> operator |(Specification<E> left, ISpecification<E> right)
        {
            return new OrSpecification(left, right);
        }

        #endregion

    } 

Here some operator overloading has been applied here is to combine the lambda expression (or predicate) of each specification (left and right side of a composite specification) to create a new lambda expression.This new lambda expression is going to use for querying. So It will help you while you are working with multiple specification already defined as your business rule.  

To use this Repository class from your business layer, one thing I must say that I am sharing a single context in the CRUD process/operation. You must keep alive the context in your entire process of a crud operation. For this, I make a UnitOfWork on top of all.

    public interface IUnitOfWork : IDisposable
    {
        IRepository<TSet> GetRepository<TSet>() where TSet : class;
        DbTransaction BeginTransaction();
        int Save();
    }

Here Unit of Work  has been introduced as umbrella over multiple repositories and shared by all repository in place of the object context directly and method like savetransaction can be switched over there. And here is that implementation. 

    public class UnitOfWork<C> : IUnitOfWork where C : ObjectContext
    {
        private DbTransaction _transaction;
        private Dictionary<Type, object> _repositories;
        private C _ctx;

        public UnitOfWork()
        {
            _ctx = Activator.CreateInstance<C>();
            _repositories = new Dictionary<Type, object>();
        }

        public IRepository<TSet> GetRepository<TSet>() where TSet : class
        {
            if (_repositories.Keys.Contains(typeof(TSet)))
                return _repositories[typeof(TSet)] as IRepository<TSet>;

            var repository = new Repository<TSet, C>(_ctx);
            _repositories.Add(typeof(TSet), repository);
            return repository;
        }
        /// <summary>
        /// Start Transaction
        /// </summary>
        /// <returns></returns>
        public DbTransaction BeginTransaction()
        {
            if (null == _transaction)
            {
                if (_ctx.Connection.State != ConnectionState.Open)
                {
                    _ctx.Connection.Open();
                }
                this._transaction = _ctx.Connection.BeginTransaction();
            }
            return _transaction;
        }

        public int Save()
        {
            return _ctx.SaveChanges();
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (null != _transaction)
            {
                _transaction.Dispose();
            }

            if (null != _ctx)
            {
                _ctx.Dispose();
            }
        }

        #endregion

    }

Repository is kind of mediator to connect business layer with data access. So your business layer should be aware of this repository class. Now, In my BLLRoleManagement class, say I have a method to delete User. That will be something like this:  

///  <summary>
/// Delete User
/// </summary>
/// <param name="userID">User ID</param>
/// <returns>True Or False</return>	   
    public static bool DeleteUser(
    string UserID)
        {
            try
            {
                var unitOfWork = new UnitOfWork<ProjectCodeEntities>();
                var userRepository = unitOfWork.GetRepository<User>();
                using (unitOfWork)
                {
                    using (var transaction = unitOfWork.BeginTransaction())
                    {
                        User UserTobeRemoved = userRepository.SelectByKey(UserID);
                        if (UserTobeRemoved == null)
                            return false;

                        userRepository.DeleteRelatedEntries(UserTobeRemoved);
                        userRepository.Delete(UserTobeRemoved);
                        if (unitOfWork.Save() >= 0)
                        {
                            transaction.Commit();
                            return true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw ErrorHandler.WrapException(ex, ErrorCodes.DB_DeleteUser_Error_Code);
            }
            return false;
        } 

Here It will initialize a ObjectContext instance which will be used in you entire process/workflow of an operation. For this, I have provided a factory method inside Unit of Work which create a repository for you using that ObjectContext . Delete all related entities associated with User and then delete User. To share the same context in this delete process, I will call this method like this  

new BLLRoleManagement().DeleteUser(userID);   

 Surely it has a lot of area to improve. I have used some interfaces here those can be used with dependency injection to decouple your code from EF. Hope its help. So here is my end of the discussion on repository pattern implementation with Entity framework. Good luck. 

References: 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Morshed Anwar
Team Leader Adaptive Enterprise Limited (www.ael-bd.com)
Bangladesh Bangladesh
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionObjectQuery Vs IQueryablememberMember 425624822 Jan '10 - 15:19 
Fantastic article. It has proved invaluable for my project, however I have a question,
 
Can you please explain why you have chosen to return an ObjectQuery(Of T) for DoQuery rather than IQueryable(Of T)?
 
From what I have read (and experienced in my project), the type ObjectQuery cannot be mocked as it is not an abstract class whereas it's base class IQueryable can be, I have made this change in my project and it has passed the litmus test (ie I can still retrieve data).
AnswerRe: ObjectQuery Vs IQueryablememberMorshed Anwar26 Jan '10 - 0:39 
No specific reason. Thanx for sharing your experience. When the query has been processed, it's no longer an IQueryable, becomes an ObjectQuery.But At design time, the compiler recognizes that it's a LINQ query and therefore assumes the return will be an IQueryable. So sure you can change/cast it into IQueryable.
GeneralSort ExpressionmemberAMBS - TI28 Dec '09 - 6:41 
I'm developing a prototype using your repository implementation and I', having some difficulties in understating how to pass a sortExpression to the Selectall method when I want to sort, for example, by a DateTime field?
 
Since de lambda expression is expecting an object type, it automatically inserts “Convert” into the expression.
 
I’m using the following code:
 
return repository.SelectAll(o => o.NewsDate, 10, 1);
 
Thnsk for the help.
 
Thanks,
Antonio
GeneralRe: Sort ExpressionmemberMorshed Anwar30 Dec '09 - 20:36 
I didn't understand ur sample code. Honestly speaking I cant get time to run my sample code to reproduce the issue. If you need a quick solution for this . please check the forum post -
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/b3537995-2441-423d-8485-ee285cf2f4ba[^]
 
is that similar to the issue ? post the error details. You can also mail me the details at - simplyananto at yahoo dot com.
I will see into this issue whenever I will get some time.
GeneralRe: Sort ExpressionmemberAMBS - TI31 Dec '09 - 1:49 
The error message is: Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types.
 
Thanks
GeneralRe: Sort Expressionmembernamesemail18 Jan '10 - 6:58 
IList<Project> list = 
          (from x in _repository.DoQuery(p => p.StatusDate, maxrows, startrow)
            select x).ToList();
 
So far this Repository implementation is impeccable and fits very well in to a solution I am working on. Thank you for your effort; it has helped a lot.
 
I am having trouble with Ordering/Sorting however; similar to Antonio's problem.
 
When I try to run the above code, I get the same Casting Error already mentioned in this thread. (unable to cast to System.DateTime)
 
None of the methods using "sortExpression" allow sorting, and any DoQuery(int maxrows, int startrow) calls fail with a message stating you must "OrderBy" before you "Skip".
 
I'm sure it's something I'm doing but could you maybe provide a working example of your code, using the DoQuery(sortExpression, maxrows, startrow) method?
GeneralRe: Sort ExpressionmemberMorshed Anwar19 Jan '10 - 2:22 
I am very much sorry for the trouble. I am going to change the method signature and make it Generic in place of 'object' type as result parameter. The change will be like this -
 
previous signature was -

ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression);
ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression,
int maximumRows, int startRowIndex);
IList<E> SelectAll(Expression<Func<E, object>> sortExpression);
IList<E> SelectAll(Expression<Func<E, object>> sortExpression,
int maximumRows, int startRowIndex);

 
New methods will be -

ObjectQuery<E> DoQuery<T>(Expression<Func<E, T>> sortExpression);
ObjectQuery<E> DoQuery<T>(Expression<Func<E, T>> sortExpression, int maximumRows, int startRowIndex);
IList<E> SelectAll<T>(Expression<Func<E, T>> sortExpression);
IList<E> SelectAll<T>(Expression<Func<E, T>> sortExpression, int maximumRows, int startRowIndex);

 
So now these method will be sothing like these
        /// <summary>
        /// Query Entity in sorted Order
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="ErrorCode">custom Error Message</param> 
        /// <returns>Collection of Entities</returns>
        public ObjectQuery<E> DoQuery<T>(Expression<Func<E, T>> sortExpression)
        {
            if (null == sortExpression)
            {
                return ((IRepository<E, C>)this).DoQuery();
            }
            return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery().OrderBy<E, T>(sortExpression);
        }
        /// <summary>
        /// Query All Entity in sorted Order with Paging support
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection Of entites</returns>
        public ObjectQuery<E> DoQuery<T>(Expression<Func<E, T>> sortExpression, int maximumRows, int startRowIndex)
        {
            if (sortExpression == null)
            {
                return ((IRepository<E, C>)this).DoQuery(maximumRows, startRowIndex);
            }
            return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery<T>(sortExpression).Skip<E>(startRowIndex).Take(maximumRows);
        }
        /// <summary>
        /// Select All Entity in sorted Order
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="ErrorCode">custom Error Message</param> 
        /// <returns>Collection of Entities</returns>
        public IList<E> SelectAll<T>(Expression<Func<E, T>> sortExpression)
        {
            if (null == sortExpression)
            {
                return DoQuery(sortExpression).ToList();
            }
            return DoQuery(sortExpression).ToList();
        }
        /// <summary>
        /// Select All Entity in sorted Order with Paging support
        /// </summary>
        /// <param name="sortExpression">Sort Expression/condition</param>
        /// <param name="maximumRows">Max no of row to Fetch</param>
        /// <param name="startRowIndex">Start Index</param>
        /// <returns>Collection Of entites</returns>
        public IList<E> SelectAll<T>(Expression<Func<E, T>> sortExpression, int maximumRows, int startRowIndex)
        {
            if (sortExpression == null)
            {
                return DoQuery(maximumRows, startRowIndex).ToList();
            }
            return DoQuery(sortExpression, maximumRows, startRowIndex).ToList();
        }
 
I am going to update the example source also. Please let me knoe if you get any trouble to work with this. Download the new example source
GeneralRe: Sort ExpressionmemberSteveInTexas20 Jan '10 - 4:13 
Bingo. Those changes work great.
 
Thanks again Morshed.
GeneralRe: Sort ExpressionmemberMorshed Anwar20 Jan '10 - 18:37 
no problem. Smile | :)
GeneralPlease rate this Article whatever you think on this.memberMorshed Anwar26 Nov '09 - 17:53 
Please rate this Article whatever you think about this article.That will help me to write my next one.
GeneralExcellentmembercyrilvincent26 Nov '09 - 9:37 
I'm French, my English is very badUnsure | :~
 
I have the same class for NHibernate & Linq2Sql
But I never know how to do the SelectByKey method for EF : Thanks a lot Laugh | :laugh:
 
Question : I want to inherits a entity object generated by EF from a POCO AbstractClass or Interface, is it possible ?
 
Cyril
GeneralRe: Excellent [modified]memberMorshed Anwar26 Nov '09 - 17:37 
no problem Cyril....my pleasure to share my thoughts wid u.
I am Bangali, and my english is also very bad:S. :P
so the answer is , I never try the scenario you have mentioned.
But I think it should work.EntityObject implements the 3 interfaces- IEntityWithChangeTracker, IEntityWithKey,IEntityWithRelationships. Apart from these interfaces, EntityObject also implements StructuralObject which its uses to set field values for properties as Zeshaan said in his tutorial.
you have to marked the class with EdmEntityTypeAttribute which tells entity framework that your POCO class in an entity. EdmEntityTypeAttribute attribute takes two parameters. First parameter represents the namespace where the entity resides and second parameter specifies the name of the entity.
 
Md. Morshed Anwar
 
modified on Thursday, November 26, 2009 11:47 PM

QuestionBest way to implement eager loadingmembertr1stan9 Nov '09 - 5:30 
After looking through your code, which has been a great read btw, I can't seem to see how you approach the issues of eager loading child object using this generic repository.
 
Am I missing something here, or is it outside of the scope of the repository interface?
 
I was thinking about something like, in a very basic form to illustrate an idea:
 
   
public ObjectQuery<E> DoQuery(string include)
{
   return entities.CreateQuery<E>("[" + typeof(E).Name + "Set]").Include(include);
}
 
or perhaps using a string array to construct a dot-seperated list for the Include method.
AnswerRe: Best way to implement eager loadingmemberMorshed Anwar10 Nov '09 - 23:40 
Thank you so much for the sharing. Yes you are right. Here I didn't implement any approach of eager loading child object. This was just an sample. You can add more methods if u need.
The example that you have provided is ok but I wanna share something with you -
"[" + typeof(E).Name + "Set]" is ok but EDMX has the option to change the entity set name. Its better to retrieve the entity set name from metadata query.
 
To Avoid the string parameter ,you can consider a very good read here
GeneralRe: Best way to implement eager loadingmembertr1stan16 Nov '09 - 2:14 
You're right, and thank you for the brilliant reference.
GeneralRe: Best way to implement eager loadingmemberMorshed Anwar17 Nov '09 - 0:52 
My pleasure Smile | :)
GeneralRe: Best way to implement eager loadingmembertr1stan17 Nov '09 - 1:30 
I'm sorry to trouble you again with what might be a really silly question. It's along the same lines so I've kept in the same thread.
 
I would like to use the repositoryName.SelectByKey(id) method to retrieve an entity, but I need to "include" the related objects during the retrieval, so something like:
 
repositoryName.SelectByKey(id).Include(inc=>inc.ForeignObject1).Include(inc=>inc.ForeignObject2)
 
Your link to create an extension for the ObjectQuery object[^] works really well, so I was thinking about something similar for SelectByKey which returns an entity type.
 
Any help would be appreciated.
GeneralRe: Best way to implement eager loadingmemberMorshed Anwar17 Nov '09 - 2:36 
You know the answer Smile | :) ..simply change return type right now for this........otherwise u have to go for EntityObject Extension (for EF V1).
GeneralSwitch to ADOmemberDeltaoo4 Nov '09 - 9:32 
Hey great article, can you use the same interfaces and create a native ADO implementation? one that uses IDbCommand and Connection and returns Data table and rows. How can you implement the context and IQuery stuff?
GeneralCode to downloadmembergoranka14 Sep '09 - 3:44 
Hello Morshed.
Really great article.
I have a bit problem understanding how do you use your repository classes(coupled with specification interface implementation) further.
Is it possible for you to paste some working code download for us?.
Thanks.
AnswerRe: Code to downloadmemberMorshed Anwar27 Sep '09 - 19:33 
Sorry for my late reply. I was on a big holiday. After I have submitted this article, I have not found any option to upload that with my article. I Am very much new to write article here. I have upload the sample code in my sky drive. here is the link- Click here to Get the Sample code of Repository Implementation[^]
 
Please , let me know if you find any difficulty to download this sample code.
Generalpublic static bool DeleteUsermemberfabry12 Sep '09 - 4:34 
First of all many thanks for very good article.
 
I'm trying to using it in a sampe application and I have some question:
 
- Is it right the "static" in the DeleteUser's BLL sample? in previous text You write about initializing all repositories in business class and at the end You write about new BLLRoleManagement().DeleteUser(userID) . So Business Logic's classes are not static right?
 
- Using the sample generic implementation of Repository I have many error when ISpecification it's used, probably I did not understand something. For example :
 
public ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
{
return
(ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
.Where(where.EvalPredicate);
}
 
Give me an error because Where method accept as parameter string, params ObjectParameter[].... and where.EvalPredicate is not. There are many other methods giving errors like this.
 
I hope You can explain more to me.
Many thanks
Fabrizio
AnswerRe: public static bool DeleteUsermemberMorshed Anwar14 Sep '09 - 0:24 
Your first Question was...Business Logic's classes are not static right?
Answer: In my example...right.....Business logic classes is not static here.I have created a Business logic class instance for each workflow and take a the Context Instance with that...and have kept it alive in whole workflow.
I can't say that its wrong if you take that as 'static'. It depends how You want to manage your workflow. For me its good to make it non-static class.
I have not written any Extension method here for CreateQuery().If you pass only the table name as First parameter, it will return all rows available in that table. Second parameter is optional and for this query you don’t need to send any. It should be worked; I have already using this code in my project.Can you please send me the exact error message? Please make sure that you have the correct reference.
I have implemented this pattern in my project and have written this article to share with you and get all of your valuable feedbacks.
GeneralRe: public static bool DeleteUsermemberfabry14 Sep '09 - 5:49 
Thank You for response.
 
I try to post some sample methods generating error and relative error description:
 
Method:

public ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
{
return
(ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
.Where(where.EvalPredicate);
}

Error:
The best overloaded method match for 'System.Data.Objects.ObjectQuery<E>.Where(string, params System.Data.Objects.ObjectParameter[])' has some invalid arguments
 
Method:

public ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression)
{
if (null == sortExpression)
{
return ((IRepository<E, C>)this).DoQuery();
}
return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery().OrderBy
<E, object>(sortExpression);
}

Error:
cannot convert from 'System.Linq.Expressions.Expression<System.Func<E,object>>' to 'System.Func<E,object>
 
The Repository class is the one posted with the article without any modification....
I'm sure I'm doing something wrong but I can't figure what.
 
Thank You
AnswerRe: public static bool DeleteUsermemberMorshed Anwar27 Sep '09 - 19:40 
Sorry for my late reply. I was on a big holiday. I thnk its better to understand if you see a sample code. After I have submitted this article, I have not found any option to upload that with my article. I Am very much new to write article here. I have upload the sample code in my sky drive. here is the link- Click here to Get the Sample code of Repository I
Please , let me know if you find any difficulty to download this sample code.
Click here to Get the Sample code of Repository Implementation[^]
GeneralRe: public static bool DeleteUsermembermschoudry10 Feb '10 - 22:05 
Hello Fabrizio,
did you find any solution for these issues?
Regards,
MS Chaudhary
MS Chaudhary

AnswerRe: public static bool DeleteUsermemberMorshed Anwar10 Feb '10 - 22:47 
It has been fixed. Please download and check The source code. Smile | :)
GeneralKey field implementationmemberricardog@mvps.org10 Aug '09 - 4:13 
Nice article Morshed,
 
anyway, I would like to add a simple mechanism to identify the Key field of an entity type when it's name is not ID in the generic repository implementation to make it a little more generic:
 
actual impl:
private string _KeyProperty = "ID";
 public string KeyProperty
        {
            get
            {
                return _KeyProperty;
            }
            set
            {
                _KeyProperty = value;
            }
        }
 
recomended impl:
 
private string _KeyProperty = "";
 
        public string KeyProperty
        {
            get
            {
                if (_KeyProperty == "")
                    this._KeyProperty = this.GetKeyName();
                return _KeyProperty;
               
            }
            //set
            //{
            //    _KeyProperty = value;
            //}
        }
protected string GetKeyName()
        {
            string retVal = "ID";
            Type t = typeof(E);
            PropertyInfo[] prop = t.GetProperties();
            foreach (PropertyInfo pi in prop)
            {
                object[] atts = pi.GetCustomAttributes(false);
                if (atts != null)
                {
                    foreach (Attribute at in atts)
                    {
                        if (at is EdmScalarPropertyAttribute)
                        {
 
                            if (((EdmScalarPropertyAttribute)at).EntityKeyProperty)
                                retVal = pi.Name;
                            break;
                        }
                    }
                }
                if (retVal != "ID")
                    break;
            }
            return retVal;
        }
 
replace the signatures of some methods in the interface and impl that restrict the key type to string:
 
public E SelectByKey(string  Key)  -->  public E SelectByKey(object Key)
public bool TrySameValueExist(string fieldName, object fieldValue, string key) -->  public bool TrySameValueExist(string fieldName, object fieldValue, object key)
void DeleteRelatedEntries(E entity, ObservableCollection<string> keyListOfIgnoreEntites); -->void DeleteRelatedEntries(E entity, ObservableCollection<object> keyListOfIgnoreEntites);
 
then, replace the references to this._KeyProperty by this.KeyProperty, this includes :
 
public E SelectByKey(object Key)
public bool TrySameValueExist(string fieldName, object fieldValue, object key)
void DeleteRelatedEntries(E entity, ObservableCollection<object> keyListOfIgnoreEntites);
 

cheers,
 
Ricardo Gonzalez
VC++ MVP
rgonzalez@mvps.org
GeneralRe: Key field implementationmemberMorshed Anwar10 Aug '09 - 18:46 
Thanx Ricardo Gonzalez . Thats a great contribution!! To be very honest that I am very much new to this "Codeproject" site and I haven't found any option to edit my article after I submitted this. I just can say your recommandation is very much appreciable and its much much better approach than what I have written. Thank you so so much for your contribution. I will be so much glad and honored if you help me such a way in future. Smile | :)
GeneralRe: Key field implementation [modified]membergeotri16 Apr '10 - 20:36 
Verry good!
modified on Saturday, April 17, 2010 3:03 AM

QuestionCan you give more examples for Repository Methods?memberJanJankovsky2 Aug '09 - 9:45 
thank you very much for this Pattern
could you give more examples for Repository Methods?
for ex. how to implamint Query like this
var q = _ctx.Orders.Incloud("OrderDetails.CreationDates");
 
modified on Monday, August 3, 2009 8:35 AM

AnswerRe: Can't Add entity to contextmemberMorshed Anwar2 Aug '09 - 20:52 
U welcome Smile | :) . ok can you please explain me the details of the exception that u got while adding entity?
JokeRe: Can't Add entity to contextmemberJanJankovsky3 Aug '09 - 2:40 
Sorry it was my misstake: just forgot transaction.Commit(); Smile | :)
QuestionImplementing Repository Pattern With Entity Frameworkmembermichael432124 Jul '09 - 11:49 
Morshed, thanks for the article, can you please show what's behind the following line of the code
_ctx = Helper.SecurityContextInstance;
 
what's the implementation behind it?
 
thanks
AnswerRe: Implementing Repository Pattern With Entity FrameworkmemberMorshed Anwar26 Jul '09 - 18:25 
It just create an instance of the container/Context of my Db mapper. Its completely depend upon of your implementation. You can make it as singleton or you can create new instance for each workflow. I have mentioned in the article that you need to maintain a single instance in your each workflow , so that all crud operations are marked as objectStateEntry in the single context(if you need to use the cache entries). Here I have created the new instance with my connection string and for each workflow I have created a new instance and have kept it alive.

public static JerichoSecurityEntities SecurityContextInstance
{
get
{
return new JerichoSecurityEntities(GenericDataAccessHelper.GenerateConnectionString(JerichoSecurityResource.DefaultSecurityDBName));
}
}
GeneralRe: Implementing Repository Pattern With Entity Framework [modified]membermichael432117 Aug '09 - 12:55 
Thank you Morshed!
 
This is pretty awesome piece of work along with SelectByKey enhancement it pretty much rocks. I think many more people would've studied/adapted it, if there was a code for download ... not a biggy though, but there are instances where i had to hunt for namespace references, not to mention the WindowsBase.dll one to resolve ObservableCollection class IDE highlight warning.
 
Cheers,
Michael
 
modified on Monday, August 17, 2009 7:03 PM

AnswerRe: Implementing Repository Pattern With Entity FrameworkmemberMorshed Anwar27 Sep '09 - 19:35 
Sorry for my late reply. I was on a big holiday. I thnk its better to understand if you see a sample code. After I have submitted this article, I have not found any option to upload that with my article. I Am very much new to write article here. I have upload the sample code in my sky drive. here is the link- Click here to Get the Sample code of Repository I
Please , let me know if you find any difficulty to download this sample code.
Click here to Get the Sample code of Repository Implementation[^]
GeneralExcellent Articlemembersetu_raas11 Jun '09 - 17:43 
I really like the way you implement the repository pattern with EF and the way you expose all the methods. Very good work. You got my 5.
 
Setu

GeneralRe: Excellent ArticlememberMorshed Anwar11 Jun '09 - 18:20 
Thank u so much for the Kind inspiration!! Smile | :) ....its morning here,ur comment just make my day Big Grin | :-D
Generala simple diagram will help skimmingmembermisaxi10 Jun '09 - 22:01 
refer to the diagram, the reader can quickly get the idea instead of reading the book you mentioned.
 
anyway, good job.
GeneralRe: a simple diagram will help skimmingmemberMorshed Anwar10 Jun '09 - 23:30 
thanx .... Yah u r right. Ok I will update this article with some diagram when I get some free time.nywaz than onca again to read out this article and ur kind suggestion. Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 2 Nov 2012
Article Copyright 2009 by Morshed Anwar
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid