Repository Pattern with Entity Framework Code-First in Composable Service End





5.00/5 (9 votes)
Implement Repository, UnitOfWork with Audit support in Composable Service End
Introduction
Some days ago, I was thinking of designing a data access layer in Composable service end and also decided to use Entity Framework Code-First approach since I hadn't tried that yet after it has been released. That’s why I plan some interfaces act as a contract between composable parts so that the client can create proxy by Export
and Import
using MEF. Besides the service contract, I plan to do the same job by using some interfaces I need to provide that data and wish to avoid coupling and dependency and here are 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 reasons I choose to go in Composable environment:
- Removes the need for a component to locate its dependencies or manage their lifetimes.
- Allows swapping of implemented dependencies without affecting the component.
- Facilitates testability by allowing dependencies to be mocked.
- Increases maintainability by allowing new components to be easily added to the system.
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
Environment
Model
I have chosen a very small environment where I just have two model of BlogPost
and Category
and also an Audit Model which represents the audit log generated by the system when any changes have been made in the 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 to 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 added as extension of InstanceContext.
Let's take a deeper look into that extension class and you can see that the composition part of our service resides 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 in this way:
[Export]
public class LogicServiceClient : ClientBase<ILogicService>
{
public virtual ILogicService Invoke
{
get { return this.Channel; }
}
}
And client can use the service by the following property:
[Import]
internal Lazy<LogicServiceClient> ProxtService;
and call like this:
var result = ProxtService.Value.Invoke.SetData(post);
var resultPost = ProxtService.Value.Invoke.GetBlogPosts();
So now, let's 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 this stuff. So we are hoping to code for data access which 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. According to Martin Fowler, the Unit of Work pattern "maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems." One of the big reasons I am using here is that I want to encapsulate the specifics of your persistence tooling from the rest of the application. This extra encapsulation is to make it easier to swap out persistence technologies later also to promote testability in our system.
The value of using a Unit of Work pattern is to free the rest of your code from these concerns so that you can otherwise concentrate on business logic. So let's 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
, features like commit, transaction and audit option are handled by IContext
which generally represents DbContext
and we will see its implementation later below. GetRepository
() method is going to create IRepository
of entity-set and keeping those object references into Dictionary
in reason to re-use if anyone wants it later. The UnitOfWork
implements the IDisposable
interface to dispose and release all the resources of the DbContext
instances also with Transaction 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, let's concentrate into our IRepository
interface. Repositories help us with code re-usability 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.
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 that stuff related to entity-set have been kept here like Create
, Update
, and Delete
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 methods, IContext
instance is providing the entity-set of type- IDbSet
by the flowing method:
this.Context.GetEntitySet<T>();
and during Remove
and Update
, it calls method to change state of the entry. So here is the 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
have used this IContext
which originally provides some services of DbContext.
To avoid dependency of DbContext
, IContext
has been introduced which provides 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 to the ObjectContext
and use it to 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's see the implementation 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)
are only two methods that are currently used in Repository
class to provide DbSet
and changing state respectively and all others providing support for IUnitOfWork
like creating transaction and commit changes into the database. Here, IObjectContextAdapter.ObjectContext
. This Connection
has been used to get DbConnection
which is the existing instance of connection used in DbContext
and you need to manually open this connection before beginning Transaction. On the other hand, DbContext.Database.Connection
is factory that provides a new connection instance and you are not allowed to use that transaction from there in parallel connection environment.
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 Primary 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.Modified,
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. So keyName
is going to hold primary key name and this name will help us to retrieve primary key value to store it as RecordID
. For deleted and modified entry dbEntry.OriginalValues.GetValue<object>(keyName)
and dbEntry.OriginalValues.GetValue<object>(keyName)
is for finding out primary key value here. From the entry, Original and Current value's comparison modified column has been detected and for each columns modification, we generate audit logs. Please not that if you made any changes in offline and try to mark it as modified, you have to Attach into context by calling Attach(entity)
. On calling of this Attach()
method, it will populate current and original properties which will be logged during audit. Otherwise, it will fail to detect the real changes of properties.
I think other portions of this code are self descriptive.
At the end, the whole picture of dependency will be like this:
And here is its dependency matrix:
Still, I believe it has a lot more area to improve. You can also try to make it independent of IDbSet
and DbTransaction
to encapsulate by introducing interfaces.
Getting Performance Benefit from DbContext in Distributed Architecture
Since I am talking about the distributed architecture here, I would like to remind you about the performance benefit features that come with DBContext
and it will also help you to extend your repositories and queries. Here is the following:
No Tracking and Auto-Compiled Queries
In my early version of repository pattern, I have used ObjectQuery
to get the features of MargeOption
and EnablePlanCaching
properties.
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. EF Team has realized the needs here and they have provided you an extension method of IQueryable
– AsNoTracking()
[Where(expression).AsNoTracking()
].
Setting EnablePlanCaching
to true
has been used for enabling the caching plan first time it has been executed and next there was no more complexity for creating TSQL from LinqToEntities
. You could also achieve this by using CompiledQuery
Class to re-use precompiled query. Unfortunately, there is no way to use CompiledQuery
in DbContext
API. CompiledQuery
works only with ObjectContext
. If we are using Code First, we are using the DbContext
API. EF team has provided auto-compiled queries, which work very differently than CompiledQuery
. Instead of your writing code to compile each query and then invoking each as needed, Entity Framework caches the generated SQL for you as a background process, then searches the cache for already compiled queries when you execute any query. It solves many issues of pre-compiled queries and gives us flexibility to cached 800 queries Item. And the most wonderful news is that you don’t need to write any code or project conversion to 4.5 to achieve this. All you need is .NET 4.5 runtime in your machine.
Find Method
As you know, Find
method is providing you to find object with its key and there also lies a performance benefit here. The Find
method will first look in memory for a matching object that’s being tracked by the context. If that’s found in memory, then Entity Framework won’t bother querying the database. So, no more visit to database and mapping complexity if it is already there in my memory.
Turn off Detect Changes and Validation
I have found DBContext
more organized and friendly in place of ObjectContext
. Now user has the full flexibility to turn off and turn on the calling of change detection and validation. You can find this under the Property Configuration
. Change tracking and the DetectChanges
method are a part of the stack where there is a penalty of performance. For this reason, it can be useful to have an idea of what is actually going on such that you can make an informed decision on what to do if the default behaviour is not right for your application. If your context is not tracking a large number of entities, you pretty much never need to switch off automatic DetectChanges
, otherwise I will suggest you turned it off and call DetectChanges
where it seems necessary.
First level Caching
There are also some features like Local
property which provide you the first level in-memory caching objects available in state entries without the deleted marked objects and you can get the benefit if you want during the lifetime of a Context
instance.
OK, that is it. Thanks for reading.
References
- Composable Entities with Entity Framework
- Entity Framework Code First Bootstrapping
- Implementing Repository Pattern With Entity Framework
- Managing Dependencies Between Components
- The Repository Pattern
- A Few of My Favorite Things... in the Entity Framework 4.2 DbContext
- Performance Considerations for Entity Framework 5
History
- 3rd October, 2012: Initial version