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   
GeneralMy vote of 4memberSponaris20 Feb '13 - 23:00 
Good article . Nice code.
Thanks
GeneralMy vote of 4memberGastonV7 Feb '13 - 20:43 
Good references in the article. Nicely written, thanks!
GeneralRe: My vote of 4memberMorshed Anwar8 Feb '13 - 11:19 
thanks a lot!! Feel free to put your feedback and let me know if you have found some points of improvement. Thanks again and good luck
Md. Morshed Anwar | Senior Software Engineer
Adaptive Enterprise Limited
Blog: http://morshedanwar.wordpress.com/

QuestionFlaws of your designmemberVahid_N1 Nov '12 - 10:08 
- Illusion of hiding details/abstracting the ORM. You can not change the in use ORM easily because you are using the repository classes. Different ORMs have different LINQ implementations and capabilities. NHibernate has 2nd level caching capability and EF, not. EF has different 1st level caching implementation than NHibernate. NHibernate does not support a lot LINQ features of EF and so on.
- Working with single entities and do not using the unit of work pattern. In real world you need to work with multiple entities in one transaction and SaveChanges method should be called at the end of the unit of work and not in the repository classes.
- Also do not using the UOW causes multiple round trips to the database instead of using just one connection per transaction.
- When you are disposing the ctx in the repository class, it will be impossible to use dependency injection across multiple classes in web applications.
AnswerRe: Flaws of your design [modified]memberMorshed Anwar1 Nov '12 - 10:25 
I have updated code with UnitOfWork which point was already mentioned in the end of previous Revision. Another Code First Version has been written by me with the other points you have noted:
Repository pattern with Entity Framework Code-First in Composable Service End
Md. Morshed Anwar | Senior Software Engineer
Adaptive Enterprise Limited
Blog: http://morshedanwar.wordpress.com/


modified 2 Nov '12 - 6:49.

QuestionRe: Flaws of your designmemberSentenryu5 Nov '12 - 5:11 
can you provide me with a link to a article that implements the UoW pattern the right way? i'm searching right now, but i'm alread having problems with repositories and a good push on the right direction would help a lot.
I'm brazilian and english (well, human languages in general) aren't my best skill, so, sorry by my english. (if you want we can speak in C# or VB.Net =p)

GeneralMy vote of 5membervijaysutaria1 Nov '12 - 3:45 
Very good and helpful article.
GeneralRe: My vote of 5memberMorshed Anwar1 Nov '12 - 3:53 
Thanks Vijay!! Thanks a lot Smile | :)
Md. Morshed Anwar | Senior Software Engineer
Adaptive Enterprise Limited
Blog: http://morshedanwar.wordpress.com/

QuestionGreat Codememberpradeepvpanzade31 Oct '12 - 6:02 
Can you please attach code for the project. I was unable to compile the classes.
AnswerRe: Great Code [modified]memberMorshed Anwar1 Nov '12 - 1:09 
I have updated this article with sample code. Some portion of code has been updated For example: Get EntitySet-Name is currently retrieving from meta data and AND/OR support has been added to specification class(In .net 3.5 there was a limitation to work operator overloading of Expression, which has been resolved in later version). Also I have added some code explanation which I did avoid in my previous revision.
Hope that will help. Good luck.
Md. Morshed Anwar | Senior Software Engineer
Adaptive Enterprise Limited
Blog: http://morshedanwar.wordpress.com/


modified 1 Nov '12 - 10:12.

SuggestionRepository for Entity Framework Code-first (DbContext)memberMorshed Anwar3 Oct '12 - 21:46 
Please note, A Entity Framework- Code First Version has been written by ME as
Repository pattern with Entity Framework Code-First in Composable Service End
Md. Morshed Anwar | Senior Software Engineer
Adaptive Enterprise Limited
Blog: http://morshedanwar.wordpress.com/

SuggestionSuggest that you update it for dbContext & MVC4memberMember 8897653 Aug '12 - 13:21 
Picking up on your most recent comment:
 
It has been published about three years ago and it has been writing on ef v1.1.
 
Given the amount that's changed since the original post:
-- The Opinions and Conventions of MVC have matured
-- EF Code First makes EF far more accessible (and therefore: mainstream)
-- 3rd party utilities have matured
-- Experience levels of senior-level devs has matured
 
The rigor and thoroughness you've applied to the explanation would be all that much more valuable if you undertook a more comprehensive re-write.
GeneralRe: Suggest that you update it for dbContext & MVC4memberMorshed Anwar3 Oct '12 - 21:43 
A Entity Framework- Code First Version has been written by ME as
Repository pattern with Entity Framework Code-First in Composable Service End
Md. Morshed Anwar | Senior Software Engineer
Adaptive Enterprise Limited
Blog: http://morshedanwar.wordpress.com/

Question[My vote of 1] sorry but no...memberLeonardo Paneque11 May '12 - 10:15 
Sorry but I can not give this more than 1.
 
this is a typical case of over-architecting.
A pattern is useful not just because you can use it to prove you can do "correct design" or using the "good practices", it is useful because it can give you something: flexibility, speed, test-ability, scalability, etc.
 
you need to consider that EF already have Repository and UoW implemented. so, if you are about to wrap that in a second layer. That should be "easier" to use than just using EF directly.
 
Because of "repositories" like this is that some developers like Ayende hate the repository pattern http://ayende.com/blog/3955/repository-is-the-new-singleton[^]
 
I disagree with Ayende's extremist point view, I think you can use repository and UoW and make some good out of it (sometimes). But it needs to be done more simple.
 
You have used hundreds of lines of code for something that is achievable on less than 30 lines of code (including IoC, UoW and POCO). No to mention what you have done with the KEY...
 
Sorry but if you are using a pattern to is supposed to make things easier, and is not doing it, then you are using it the wrong way.
Leonardo Paneque

AnswerRe: [My vote of 1] sorry but no... [modified]memberMorshed Anwar12 May '12 - 2:10 
Thanks for your judgement!! its not on "good practices" mood,it help me to have a single entry point of all
db related code, implementing Logging and Caching , audit trial ....its totally up to you whether you want it or not ....its just an concept that I share and Im sure it helps some people get some ready to use code. ....yes, Surely , I also feel there is always some areas of improvement...and this article has that also....I am not worry about anybody's vote , all I am worry about reader's feedback that I may missed here. It has been published about three years ago and it has been writing on ef v1.1.

modified 12 May '12 - 9:24.

AnswerRe: [My vote of 1] sorry but no...membernportelli3 Apr '13 - 5:52 
So EF implements UoW and Repository patterns, or so I've heard. Yet I've never seen a good example of how to use EF properly WITHOUT it being directly in the presentation layer. In my case a MVC project. Please if you know of a simpler implementation, share it.
GeneralRe: [My vote of 1] sorry but no...memberLeonardo Paneque3 Apr '13 - 6:38 
They do implement those patterns, a DbContex or ObjectContext would be your UoW, and the ObjectSet or Set inside are your repositories, then, there is no need to rebuild the pattern around the pattern.
 
so, if you have a UoW you use it in your site from the presentation layer right? why not EF then? now, if the problem is that you dont want dependencies/references to EF (which makes sense) I would recommend to use Unity Framework and inject those into your project. All you need to do is wrap that around with an interface that return IQueryable.
 
IQueryable is the center of all here, is what makes those pattern-wrapping obsolete, when Martin Fowler wrote that in the book, he referred to "collections", and IQueryable is more powerful than that.
Leonardo Paneque

GeneralRe: [My vote of 1] sorry but no...membernportelli3 Apr '13 - 7:54 
Actually our presentation layer would call services, which would then call the DB or UoW, but same difference. Abstracting out EF is not my intention, I'd rather have testable code. IE I don't want to have to use a database to test code. I've always assumed this was the reason people implemented their own UoW and Repository patterns. I suppose I'll go look and see how testable EF is on it's own.
 
And I certainly plan on using a dependency injection framework. Unfortunately I learn from example and most examples out there are so trivial they are useless when it comes to wiring all this up in a real app that doesn't get things one entity at a time.
 
Thank you.
QuestionGreat!memberLaserson24 Jan '12 - 7:18 
Nice article, thanks.
 
But how can i use it with dependency injection?
QuestionHow to use SQL Server View?memberSunasara Imdadhusen17 Oct '11 - 4:03 
Hi
Morshed Anwar,
 
Excellent code you have published for us. thanks for sharing with us. My vote of 5
 
Can you explain how to call SQL View using Repository Pattern and Entity Framework 4.0 wit MVC 3.0?

Any answer would be appreciated!.
Thanks,
Imdadhusen

sunaSaRa Imdadhusen
+91 99095 44184

QuestionInteresting! But would this approach work with the entities with multiple primary keys?memberRahman Mahmoodi13 Aug '11 - 16:11 
This sounds good to have a generic repository, but would this approach work for entities with multiple primary keys?
QuestionHow are you creating the _ctx object?memberkaschimer2 Jun '11 - 3:57 
I see in your code you create _ctx with:
 
_ctx = Helper.SecurityContextInstance;
 
I am new to EF and was wondering what your Helper method/property looks like??
"Those that say a task is impossible shouldn't interrupt the ones who are doing it." - Chinese Proverb

QuestionWCF?memberInian6 Apr '11 - 13:31 
Any thoughts on how to implmenet this generic repository in a WCF service? It doesn't seem to support Generics, where you have to provide a concrete type while instantiating. Thanks.
Generalgreat article - working with where, join, other clausesmemberMauricio Sanchez28 Mar '11 - 6:01 
Hello,
 
Thanks for this article, makes the pattern really easy to use. One questions though, this may sound dummy as I am new to LinQ, how can I use your example with Linq some more complex queries?
Consider this example:
var myList = from a in _db.apples
join p in _db.oranges on a.idequals p.Id
where a.color == p.color
select p ;
Or perhaps a sample that works with a function parameter
var myList = from a in _db.apples
join p in _db.oranges on a.idequals p.Id
where a.color == PARAM
select p ;
In addition would you please provide an example on how to use those methods that work with the ISpecification?
 
Thanks a lot!
GeneralRe: great article - working with where, join, other clausesmemberMauricio Sanchez30 Mar '11 - 5:51 
Never mind, I figured it out... Thanks

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

Permalink | Advertise | Privacy | Mobile
Web04 | 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