Download Source Here
Introduction
Some days ago I was thinking to design data access layer in Composable service end and also decide to use Entity Framework Code-First approach since I didn’t tried that yet after it has been released. That’s why I plan some interfaces act as a contract between composable parts. So client can create proxy by Export
and Import
using MEF. Beside the service contract I plan to do same job by using some interfaces I need to provide that data and wish avoid coupling and dependency and here the following interfaces in service implementation – IUnitOFWork ,IRepository and IContext to separate the logic that retrieves the data and maps it to the entity model from the business logic that acts on the model. Here are following reason I choose to go in Composable environment -
And Here is some reason for using repository pattern in data access layer in place direct access the database code-
Environment
Model
I have chosen a very small environment where I just have two model of BlogPost and Category and also a Audit Model which represent the audit log generated by the system when any changes has been made in database.
Data Access
Data Access is going to hold three interfaces as I have mentioned earlier – IRepository
, IUnitOfWork
and IContext
and their implementation with DbContext
and DbSet
. Here I have DbContext
named BlogContext
and their Initializer.
Using the code
So here we are going define our Composable Service first. To do that we need to extend service behavior and instance provider there. This instance provider will use a InstanceContext
extension named ServiceComposer
while creating and releasing instance.
public class ComposingInstanceProvider : IInstanceProvider { private Type serviceType; public ComposingInstanceProvider(Type serviceType) { this.serviceType = serviceType; } public object GetInstance(System.ServiceModel.InstanceContext instanceContext) { return this.GetInstance(instanceContext, null); } public object GetInstance(System.ServiceModel.InstanceContext instanceContext,
System.ServiceModel.Channels.Message message) { // Create composer: var composer = new ServiceComposer(); instanceContext.Extensions.Add(composer); // Retrieve instance: var instance = Activator.CreateInstance(this.serviceType); // Compose instance: composer.Compose(instance); // Return instance: return instance; } public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance) { // Remove composer: var composer = instanceContext.Extensions.Find<ServiceComposer>(); if (composer != null) instanceContext.Extensions.Remove(composer); // Release instance: if (instance is IDisposable) ((IDisposable)instance).Dispose(); } }
In GetInstance
method, a composer name ServiceComposer
has been created and add as extension of InstanceContext.
Lets take deeper look into that extention class and you can see that the composition part of our service reside in this ServiceComposer class.
we have used CompositionContainer
and register the AggregateCatalog
and container itself. Before you can inject dependencies into an object, the types of the dependencies need to be registered with the container. Registering a type typically involves passing the container an interface and a concrete type that implements that interface. There are primarily two means for registering types and objects: through code or through configuration. Here in this solution We will register types or a mapping with the container. At the appropriate time, the container will build an instance of the type you specify.
void IExtension<InstanceContext>.Attach(InstanceContext owner) { compositionContainer = new CompositionContainer(Settings.DefaultCatalog); compositionContainer.ComposeExportedValue(compositionContainer); } void IExtension<InstanceContext>.Detach(InstanceContext owner) { if (compositionContainer != null) { compositionContainer.Dispose(); compositionContainer = null; } }
So after using the above ServiceBehaviour
in service implementation, we will get into composable from the client end we can do Export
my service like this way :
[Export] public class LogicServiceClient : ClientBase<ILogicService> { public virtual ILogicService Invoke { get { return this.Channel; } } }
So now lets jump to the implementation of ILogicService
and the data access part to create Repository
, UnitOfWork
and DbContext
. In the implementation of service end, we would like to write code for access data with interfaces of these stuff. So we are hoping to code for data access will be look something like this -
[Import] Lazy<IUnitOfWork> _unitOfWork; public bool SetData( BlogPost post) { using (var db = _unitOfWork.Value) { db.EnableAuditLog = false; using (var transaction = db.BeginTransaction()) { try { IRepository<BlogPost> repository = db.GetRepository<BlogPost>(); repository.Add(post); int i = db.Commit(); transaction.Commit(); return (i > 0); } catch (Exception) { transaction.Rollback(); throw; } } } return false; }
We have used Lazy instance creation of UnitOfWork
here. So lets define our first interface of IUnitOfWork
-
public interface IUnitOfWork: IDisposable { bool EnableAuditLog { get; set; } int Commit(); IRepository<TSet> GetRepository<TSet>() where TSet : class; DbTransaction BeginTransaction(); }
Here this UnitOfWork
is responsible to creating transaction, commit the changes made , switch on/off audit logging and a factory-method for creating repository instances of expected entity. Here is its implementation
[Export(typeof(IUnitOfWork))] public class UnitOfWork : IUnitOfWork { [Import] private Lazy<IContext> _context; private CompositionContainer _container; private DbTransaction _transaction; private Dictionary<Type, object> _repositories; public bool EnableAuditLog { get{ return _context.Value.IsAuditEnabled; } set { _context.Value.IsAuditEnabled = value; } } [ImportingConstructor] public UnitOfWork(CompositionContainer container) { _container = container; _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 = _container.GetExportedValue<IRepository<TSet>>(); repository.Context = this._context.Value; _repositories.Add(typeof(TSet), repository); return repository; } public void Dispose() { if (null != _transaction) _transaction.Dispose(); if (null != _context.Value) _context.Value.Dispose(); } public int Commit() { return _context.Value.Commit(); } public DbTransaction BeginTransaction() { _transaction = _context.Value.BeginTransaction(); return _transaction; } }
In UnitOfWork
, feature like commit, transaction and audit option are handled by IContext
which is generally represent DbContext
and we will see it’s implementation later below. GetRepository
() method is going to create IRepository
of entity-set and keeping those object reference into Dictionary
in reason to re-use if anyone want it later.The UnitOfWork
implements the IDisposable
interface to dispose and release all the resources of the DbContext
instances also with Transection o
bject Database Connection. The Dispose() method will be automatically called because of the using
construct. It is called once scope of the UnitOfWork
terminates.
Now, lets concentrate into our IRepository
interface. Repositories help us with code reusability and improve testability because they implement interfaces that can be mocked using various mocking frameworks. If all you need is CRUD operations for your new repository then besides writing the name of new class and interface you don’t need to write anything else because your domain specific repository will be using composition approach-
Now, lets concentrate into our IRepository
interface-
public interface IRepository<T> { IContext Context { get; set; } void Add(T entity); void Update(T entity); void Remove(T entity); T FindSingle(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes); IQueryable<T> Find(Expression<Func<T, bool>> predicate = null, params Expression<Func<T, object>>[] includes); IQueryable<T> FindIncluding(params Expression<Func<T, object>>[] includeProperties); int Count(Expression<Func<T, bool>> predicate = null); bool Exist(Expression<Func<T, bool>> predicate = null); }
All those stuff related to entity-set has been kept here like Create, Update and Delete and Queries.There are two ways that the repository can query business entities. It can submit a query object to the client’s business logic or it can use methods that specify the business criteria. In the latter case, the repository forms the query on the client’s behalf. The repository returns a matching set of entities that satisfy the query. Now look into the Repository
-
[Export(typeof(IRepository<>))] public class Repository<T> : IRepository<T> where T : class { public IContext Context { get; set; } public void Add(T entity) { this.Context.GetEntitySet<T>().Add(entity); } public void Update(T entity) { this.Context.ChangeState(entity, System.Data.EntityState.Modified); } public T FindSingle(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null,
params System.Linq.Expressions.Expression<Func<T, object>>[] includes) { var set = FindIncluding(includes); return (predicate == null) ? set.FirstOrDefault() : set.FirstOrDefault(predicate); } public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null,
params System.Linq.Expressions.Expression<Func<T, object>>[] includes) { var set = FindIncluding(includes); return (predicate == null) ? set : set.Where(predicate); } public IQueryable<T> FindIncluding(params System.Linq.Expressions.Expression<Func<T,
object>>[] includeProperties) { var set = this.Context.GetEntitySet<T>(); if (includeProperties != null) { foreach (var include in includeProperties) { set.Include(include); } } return set.AsQueryable(); } public int Count(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null) { var set = this.Context.GetEntitySet<T>(); return (predicate == null) ? set.Count() : set.Count(predicate); } public bool Exist(System.Linq.Expressions.Expression<Func<T, bool>> predicate = null) { var set = this.Context.GetEntitySet<T>(); return (predicate == null) ? set.Any() : set.Any(predicate); } public void Remove(T entity) { this.Context.ChangeState(entity, System.Data.EntityState.Deleted); } }
you can see that in most of the method, IContext
instance is providing the entity-set of type- IDbSet
by the flowing method -
this.Context.GetEntitySet<T>();
and during Remove and Update it call method to change state of the entry. So here full look of IContext
interface-
public interface IContext : IDisposable { bool IsAuditEnabled { get; set; } IDbSet<T> GetEntitySet<T>() where T : class; void ChangeState<T>(T entity, EntityState state) where T : class; DbTransaction BeginTransaction(); int Commit(); }
Both UnitOfWork
and Repository
has used this IContext
which is originally provide some services of DbContext.
To avoid dependency of DbContext
, IContext
has been introduced which provide aggregate root in Repository
and UnitOfWork
. Here is the DbContext
implementation:
[Export(typeof(IContext))] public class BlogContext : DbContext, IContext { public BlogContext() : base() { IsAuditEnabled = true; ObjectContext.SavingChanges += OnSavingChanges; } public ObjectContext ObjectContext { get { return (this as IObjectContextAdapter).ObjectContext; } }.....................................
Here we have access the ObjectContext
and use it get the event before saving changes into the database. I think you can guess it has been used ..yes, right ….it is for generating the audit log based on the changes we have done. I will describe that implementation later . Let see the implemetation of IContext
-
#region IContext Implementation public bool IsAuditEnabled { get; set; } public void ChangeState<T>(T entity, EntityState state) where T : class { Entry<T>(entity).State = state; } public IDbSet<T> GetEntitySet<T>() where T : class { return Set<T>(); } public virtual int Commit() { if (this.ChangeTracker.Entries().Any(IsChanged)) { return this.SaveChanges(); } return 0; } private static bool IsChanged(DbEntityEntry entity) { return IsStateEqual(entity, EntityState.Added) || IsStateEqual(entity, EntityState.Deleted) || IsStateEqual(entity, EntityState.Modified); } private static bool IsStateEqual(DbEntityEntry entity, EntityState state) { return (entity.State & state) == state; } public virtual DbTransaction BeginTransaction() { var connection = (this as IObjectContextAdapter).ObjectContext.Connection; if (connection.State != ConnectionState.Open) { connection.Open(); } return connection .BeginTransaction(IsolationLevel.ReadCommitted); } #endregion
GetEntitySet<T>()
and ChangeState<T>(T entity, EntityState state)
is only two methods those currently is used Respository
class to provide DbSet
and changing state respectively and all others providing support for IUnitOfWork
like creating transaction and commit changes into the database.
Now, To generate audit logs into the database through the following method:
private List<Audit> CreateAuditRecordsForChanges(DbEntityEntry dbEntry) { List<Audit> result = new List<Audit>(); #region Generate Audit //determine audit time DateTime auditTime = DateTime.UtcNow; // Get the Table name by attribute TableAttribute tableAttr = dbEntry.Entity.GetType()
.GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute; string tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name; // Find Primiray key. string keyName = dbEntry.Entity.GetType().GetProperties().Single(p =>
p.GetCustomAttributes(typeof(KeyAttribute), false).Count() > 0).Name; if (dbEntry.State == System.Data.EntityState.Added) { result.Add(new Audit() { Id = Guid.NewGuid(), AuditDateInUTC = auditTime, AuditState = AuditState.Added, TableName = tableName, RecordID = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(),
// Again, adjust this if you have a multi-column key NewValue =ToXmlString( dbEntry.CurrentValues.ToObject()) } ); } else if (dbEntry.State == System.Data.EntityState.Deleted) { result.Add(new Audit() { Id = Guid.NewGuid(), AuditDateInUTC = auditTime, AuditState = AuditState.Deleted, TableName = tableName, RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(), NewValue = ToXmlString( dbEntry.OriginalValues.ToObject().ToString()) } ); } else if (dbEntry.State == System.Data.EntityState.Modified) { foreach (string propertyName in dbEntry.OriginalValues.PropertyNames) { if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName)
, dbEntry.CurrentValues.GetValue<object>(propertyName))) { result.Add(new Audit() { Id = Guid.NewGuid(), AuditDateInUTC = auditTime, AuditState = AuditState.Added, TableName = tableName, RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(), ColumnName = propertyName, OriginalValue = dbEntry.OriginalValues.GetValue<object>(propertyName) == null ? null : dbEntry.OriginalValues.GetValue<object>(propertyName).ToString(), NewValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString() } ); } } } return result; #endregion }
During audit, DbEntityEntry
has been used here. Table attribute on the model-
[Table("Categories")] [DataContract] public class Category : BaseModel {.............
has been seen to get table name and key attribute on field-
[DataContract] public class BaseModel { [Key] [DataMember] public Guid Id { get; set; ..........
has been used to get primary key. If you have multiple primary key, then you need to handle how to store them into database. Here in this example I assume single primary key for each model.
And From the entry Original and Current values has been used to generate audit logs. Ok that is it. Thanks For reading.
References
Filed under: .Net, C#, EF4, Entity Framework, Expression, WCF Tagged: code first, Composable, DbContext, EF4, EF5, Entity Framework, MEF, repository pattern, UnitOfWork
