Entity Framework and a Generic Reposi
Introduction
This article presents a high-level overview of multi-layered
.Net application that implements the Repository Pattern
along with the Entity Framework on its Data Access layer.
The application uses Inversion of Control to decouple
abstractions
(interfaces)
from
implementations.
More specifically,
it
uses
Ninject
to
implement
Dependency Injection.
This separation of concerns allows us to dissect modules
and swap them with other implementations. This is
achieved
by
relying
on
contracts
rather
than
implementations which results on code that is agnostic of
how operations are implemented behind the interfaces.
For example, if you design your business logic to rely on
an interface that follows the Repository Pattern as a
data persistence mechanism, for all intends and purposes,
your business logic will treat this dependency as a
memory domain object collection. Therefore, for
production you could provide an implementation of that
dependency that uses the Entity Framework behind the
scenes to store and retrieve data from a SQL database. For
automated unit testing, you could provide a completely
different implementation that stores and retrieves data
from in-memory objects.
Evidently, you could execute all your tests against the
database; however, this setting greatly helps isolating unit
tests and cut dependencies on other resources, such as
the database, which may need more work to setup up and
tear up for unit testing (especially true if you have other
team members).
Before moving forward, make sure you read What's
Inversion of Control? if you are not familiar with Inversion
of Control or Dependency Injection or would like to hear
more about the advantages of interface-base design
Note: This article does not get into details on how to use
all these technologies and patterns, instead it provides
references to other sources and shows you how they all fit
together in a single application.
The Repository Pattern
The Repository Pattern "mediates between the domain
and data mapping layers using a collection-like interface
for accessing domain objects".
In a multi-layered application the Repository Pattern is
implemented in the Data Access layer
(or Persistence Layer) and it interacts with the Service
Layer and the Data Source by mapping data to objects
back and forth. The following picture shows a web
application that uses a SQL database in the back end.
For each repository in the application we will create both
the interface and the implementation. The interface will
have methods such as: Add, Delete, Find, GetAll, etc. Most
methods will return objects or collections, others such as
Count will return integers. For example, the GetAll method
for the User's Repository will return the list of all User's
objects found in the database.
The operations mentioned above are common enough that
we expect them to see them in all repository classes. For
that reason, we will leverage the power of the Entity
Framework and Link to create a generic interface and a
generic class that implements this "boilerplate" code.
The Generic Interface
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IGenericRepository<t> : IDisposable where T : c
{
IQueryable<t> GetAll();
IQueryable<t> FindAllBy(Expression<Func<T, bool>> predicat
T FindFirstBy(Expression<Func<T, bool>> predicate);
T Find(int id);
void Add(T entity);
void Delete(T entity);
void Delete(IQueryable<t> entities);
void Edit(T entity);
int Save();
int Count();
void Refresh(T entity);
}
The Generic Implementation
For the generic class we will use an abstract class that
implements the generic interface in order to provide
common code. Note that this class has a constructor that
takes an IDbContextFactory. This factory allows us to
retrieve the DbContext object that is shared across
other repositories. As a side effect, the creation and
access to this object is centralized into a single location
which facilitates its life cycle management. The
following sections will provide more information on this.
?
1
2
3
4
5
6
7
public abstract class GenericRepository<t> :
IGenericRepository<t>
where T : class
{
protected static ILog log;
public DbContext Context
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{ get; set; }
public GenericRepository(IDbContextFactory dbContextFactory)
{
Context = dbContextFactory.GetDbContext();
log = LogManager.GetLogger(GetType());
}
public virtual IQueryable<t> GetAll()
{
return Context.Set<t>();
}
public virtual IQueryable<t> FindAllBy(System.Linq.Expression
{
return Context.Set<t>().Where(predicate);
}
public virtual T FindFirstBy(System.Linq.Expressions.Expressio
{
return Context.Set<t>().FirstOrDefault(predicate);
}
public virtual void Add(T entity)
{
Context.Set<t>().Add(entity);
}
public virtual void Delete(T entity)
{
Context.Set<t>().Remove(entity);
}
public virtual void Delete(IQueryable<t> entities)
{
foreach(T entity in entities.ToList())
Context.Set<t>().Remove(entity);
}
public virtual void Edit(T entity)
{
Context.Entry(entity).State = System.Data.EntityState.Modified
}
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public virtual int Save()
{
try
{
return Context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
foreach (var validationErrors in dbEx.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationEr
{
log.Error(string.Format("Property: {0} Error: {1}", validati
}
}
}
catch (Exception ex)
{
log.Error("Save failed", ex);
}
return 0;
}
public virtual T Find(int id)
{
return Context.Set<t>().Find(id);
}
public void Refresh(T entity)
{
Context.Entry(entity).Reload();
}
public virtual int Count()
{
return Context.Set<t>().Count();
}
public void Dispose()
{
if (Context != null)
92
93
94
Context.Dispose();
}
}
Usage
This is how you would declare your interface. Notice that
besides declaring the common code (Add, Delete, GetAll,
etc.) it also declares and additional method specific to this
repository GetSubordinates()
?
1
2
3
4
public interface IEmployeeRepository :IGenericRepository<employ
{
IEnumerable<employee> GetSubordinates(int id);
}
Here is the implementation, notice the constructor is
needed
here
in
order
to
obtain
an IDbContextFactory instance
through
Dependency
Injection. Evidently, the class must also implement
GetSubordinates(), this was just a simple example on how
you can leverage the generic repository class and you are
still able to introduce class-specific code.
?
1
2
3
4
5
6
7
8
9
10
11
public class EmployeeRepository : GenericRepository<employee
{
public EmployeeRepository(IDbContextFactory dbContextFactory
: base(dbContextFactory)
{}
public IEnumerable<employee> GetSubordinates(int id)
{
return (Context as DataModelContainer).GetSubordinates(id);
}
}
Sharing the DbContext object by using a
DbContectFactory
The IDbContextFactory interface is very straightforward, it
only declares a method and implements the IDisposable
interface.
?
1
2
3
4
public interface IDbContextFactory : IDisposable
{
DbContext GetDbContext();
}
The implementation is also straightforward. The
DBContextFactory creates an instance of DbContext in its
constructor. For this reason, we can use the factory class
to obtain an instance of DbContext and as long as we use
the same factory we will be sharing the
same DbContext instance across all repositories. This is
actually very easy to do with a dependency injection
container such as Ninject (briefly described in the next
section).
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class DbContextFactory : IDbContextFactory
{
private readonly DbContext _context;
public DbContextFactory()
{
_context = new DataModelContainer();
}
public DbContext GetDbContext()
{
return _context;
}
public void Dispose()
{
if (_context != null)
{
_context.Dispose();
GC.SuppressFinalize(this);
}
}
}
Using Ninject as The IoC Container
Ninject is very simple to setup. In a few words you need to
add a few dependencies to your project and a couple of
configuration files. The interesting piece is where you tell
the container the injection pattern it will use (e.g., inject
dependencies through setters or constructors) and also
how to map between interfaces and implementations. For
example, the NinjectWebCommon class provides the
following method to do this mapping:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
//repoositories
kernel.Bind<idbcontextfactory>().To<dbcontextfactory>().InRequ
kernel.Bind<iemployeerepository>().To<employeerepository>();
//... more repositories...
}
One thing to note is the use of the Request scope for the
DbContextFactory class which tells Ninject to create a
single instance of this object per HTTP request. In other
words, every time a user requests a page, no mater how
many repository classes are involved, Ninject will create a
single factory class and, therefore, a
single DbContext instance. This also ensures that the
object is destroyed once the request completes. Good
stuff..
Note: In the examples provided here I've been using
dependency injection through constructors.
Putting
it
all
Repositories
together
Using
the
Now that you have created repositories you could either
use them directly on your controllers or on a service class.
The following example shows the Employee Service
obtaining the repositories from the Dependency Injection
container.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EmployeeService : IEmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly ICompanyService _companyService;
public EmployeeService(IEmployeeRepository _employeeReposit
{
this._employeeRepository = _employeeRepository;
this._companyService = _companyService;
}
//class details here
//notice you can now do _employeeRepository.GetAll() or _compan
//they came for free :)
}
Note: You may have services that depend on multiple
repositories and this is not a problem at all since you have
the flexibility, thanks to Ninject, of deciding whether you
want to use the same instance (Singleton) or create a new
object each time one is requested (Non-Singleton)
Future Work - Unit of Work
Service classes in business logic layer tend to use multiple
repositories simultaneously and sometimes need to have
transactions that either commit all or roll back all actions.
The generic repository class presented here may be not be
transparent enough to control these transactions
efficiently. Therefore, a nice and easy-to-implement
improvement to this class is to use the Unit of
Work concept, which pretty much wraps the repositories
operations to resolve exactly this problem. More info on
this can be found here:
Using Repository and Unit of Work patterns with Entity
Framework 4.0