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.
Using the Code
Here I use the Composite Repository pattern:
public interface IRepository<E, C>
{
DbTransaction BeginTransaction();
void Add(E entity);
void AddOrAttach(E entity);
void DeleteRelatedEntries(E entity);
void DeleteRelatedEntries
(E entity, ObservableCollection<string> keyListOfIgnoreEntites);
void Delete(E entity);
int Save();
ObjectQuery<E> DoQuery(string entitySetName);
ObjectQuery<E> DoQuery();
ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where);
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, C>, IDisposable
where E : EntityObject
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();
}
public ObjectQuery<E> DoQuery(string entitySetName)
{
return _ctx.CreateQuery<E>("[" + entitySetName + "]");
}
public ObjectQuery<E> DoQuery()
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]");
}
public ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
{
return
(ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
.Where(where.EvalPredicate);
}
public ObjectQuery<E> DoQuery(ISpecification<E> where)
{
return
(ObjectQuery<E>)_ctx.CreateQuery<E>("[" + typeof(E).Name + "]")
.Where(where.EvalPredicate);
}
public ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex)
{
return (ObjectQuery<E>)_ctx.CreateQuery<E>
("[" + typeof(E).Name + "]").Skip<E>(startRowIndex).Take(maximumRows);
}
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);
}
public ObjectQuery<E> DoQuery(Expression<Func<E, object>>
sortExpression, int maximumRows, int startRowIndex)
{
if (sortExpression == null)
{
return ((IRepository<E, C>)this).DoQuery(maximumRows, startRowIndex);
}
return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery
(sortExpression).Skip<E>(startRowIndex).Take(maximumRows);
}
public IList<E> SelectAll(string entitySetName)
{
return DoQuery(entitySetName).ToList();
}
public IList<E> SelectAll()
{
try
{
return DoQuery().ToList(); ("[" + typeof(E).Name + "]");
}
catch (Exception)
{
throw;
}
}
public IList<E> SelectAll(string entitySetName, ISpecification<E> where)
{
return DoQuery(entitySetName, where).ToList();
}
public IList<E> SelectAll(ISpecification<E> where)
{
return DoQuery(where).ToList();
}
public IList<E> SelectAll(int maximumRows, int startRowIndex)
{
return DoQuery(maximumRows, startRowIndex).ToList();
}
public IList<E> SelectAll(Expression<Func<E, object>> sortExpression)
{
if (null == sortExpression)
{
return DoQuery(sortExpression).ToList();
}
return DoQuery(sortExpression).ToList();
}
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();
}
public E SelectByKey(string Key)
{
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);
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(binaryExpr,
new ParameterExpression[] { xParam });
IList<E> resultCollection = ((IRepository<E, C>)this).SelectAll
(new Specification<E>(lambdaExpr));
if (null != resultCollection && resultCollection.Count() > 0)
{
return resultCollection.First<E>();
} return null;
}
public bool TrySameValueExist(string fieldName, object fieldValue, string key)
{
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);
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
new ParameterExpression[] { xParam });
return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
}
public bool TryEntity(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>
(selectSpec.EvalPredicate);
}
public int GetCount()
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Count();
}
public int GetCount(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]")
.Where(selectSpec.EvalPredicate).Count();
}
public void Delete(E entity)
{
_ctx.DeleteObject(entity);
}
public void Delete(object entity)
{
_ctx.DeleteObject(entity);
}
public void Add(E entity)
{
_ctx.AddObject(entity.GetType().Name, entity);
}
public void AddOrAttach(E entity)
{
EntityKey key;
object originalItem;
if (entity.EntityKey == null)
{
key = _ctx.CreateEntityKey(entity.GetType().Name, entity);
}
else
{
key = entity.EntityKey;
}
try
{
if (_ctx.TryGetObjectByKey(key, out originalItem))
{ if (originalItem is EntityObject &&
((EntityObject)originalItem).EntityState != EntityState.Added)
{
_ctx.ApplyPropertyChanges(
key.EntitySetName, entity);
}
}
else
{ Add(entity);
} }
catch (Exception ex)
{
throw ex;
}
}
public DbTransaction BeginTransaction()
{
if (_ctx.Connection.State != ConnectionState.Open)
{
_ctx.Connection.Open();
}
return _ctx.Connection.BeginTransaction();
}
public void DeleteRelatedEntries(E entity)
{
foreach (var relatedEntity in (((IEntityWithRelationships)entity).
RelationshipManager.GetAllRelatedEnds().SelectMany(re =>
re.CreateSourceQuery().OfType<EntityObject>()).Distinct()).ToArray())
{
_ctx.DeleteObject(relatedEntity);
} }
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;
} } _ctx.DeleteObject(relatedEntity);
} }
#endregion
#region IDisposable Members
public void Dispose()
{
if (null != _ctx)
{
_ctx.Dispose();
}
}
#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 to the code.
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>
{
Expression<Func<E, bool>> EvalPredicate { get; }
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 Expression<Func<E, bool>> EvalPredicate
{
get { return _evalPredicate; }
}
public virtual Func<E, bool> EvalFunc
{
get { return _evalFunc; }
}
#endregion
#region Constructors
public Specification(Expression<Func<E, bool>> predicate)
{
_evalPredicate = predicate;
}
private Specification() { }
#endregion
}
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 Globalvariable of my EF ObjectContext and all using repository and in the constructor, I initialize all these variables like this:
#region Global members
Repository< User, SecurityEntities> _userRepository = null;
Repository< Role, SecurityEntities> _roleRepository = null;
Repository< Task, JerichoSecurityEntities> _taskRepository = null;
Repository< RoleInUser,SecurityEntities> _roleInUserRepository
= null;
Repository< RoleTask,SecurityEntities> _roleTaskRepository
= null;
Repository< DBAuditForSecurity, SecurityEntities> _securityAuditRepository
SecurityEntities _ctx = null;
#endregion
public BLLRoleManagement()
{
_ctx = Helper.SecurityContextInstance;
_userRepository = new Repository< User,SecurityEntities>(_ctx);
_roleRepository = new Repository< Role, SecurityEntities>(_ctx);
_taskRepository = new Repository< Task, SecurityEntities>(_ctx);
_roleInUserRepository = new Repository< RoleInUser, SecurityEntities>(_ctx);
_roleTaskRepository = new Repository< RoleTask, SecurityEntities>(_ctx);
_securityAuditRepository =
new Repository< DBAuditForSecurity, SecurityEntities>(_ctx);
}
......
..................( BLL methods , properties etc.)
Now, in my BLLRoleManagement class, say I have a method to delete User. That will be something like this:
public static bool DeleteUser(
string UserID)
{
try
{
using (DbTransaction transaction = userRepository.BeginTransaction())
{
User UserTobeRemoved = userRepository.SelectByKey(UserID);
if (UserTobeRemoved == null)
return false;
userRepository.DeleteRelatedEntries(UserTobeRemoved);
userRepository.Delete(UserTobeRemoved);
if (userRepository.Save() >= 0)
{
transaction.Commit();
return true;
}
}
}
catch (Exception ex)
{
throw ErrorHandler.WrapException(ex, ErrorCodes.DB_DeleteUser_Error_Code);
}
return false;
}
Here I just open transaction using userRepository global variable. 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);
So here is my end of the discussion on repository pattern implementation with Entity framework.