Introduction
Background
The drive behind this tutorial is to have a complete example on
Repository and Unit of Work with IoC and Unit Testing. Adding IoC
and Unit Testing will show how all these components/patterns can
work together.
The idea of using the repository pattern is to create an abstract
data access layer for your application. This will allow you to
centralise all your data access logic in one place. Combined with
generic feature you can reduce the amount of code you need for
common scenarios and still have the ability to create custom
repository for more specific usage.
The unit of work pattern helps to combine a set of interactions and
commit them at once using a transaction. If you are creating your
data source from scratch and you are using just one EF context
you probably don't need the unit of work and you can depend on
your data context however this is not always the case. For
example you may want to use more than one EF context to
complete your operations, that's where the unit of work comes in
handy. Using the transaction facility in the unit of work will allow
you to work with different contexts in the same scope without the
fear of losing your data integrity if an exception was thrown while
completing the data manipulation.
The Implementation
The implementation consists of 3 parts:
1 The model (entity classes for Team, User and Role)
2 The data layer (repository, unit of work, EF context)
3 The user interface (the console application)
The model
The model consists of 3 classes which are: User, Role and Team.
Here is the implementation for these classes
public
class
User
:
BaseModel<int>
{
public
string
Password
{
get;
set;
}
public
string
email
{
get;
set;
}
[Required,
StringLength(100)]
public
override
string
Name
{
get;
set;
}
public
int?
TeamId
{
get;
set;
}
public
virtual
Team
Team
{
get;
set;
}
public
int
RoleId
{
get;
set;
}
public
virtual
Role
Role
{
get;
set;
}
}
public
class
Team
:
BaseModel<int>
{
public
virtual
IEnumerable<User>
Users
{
get;
set;
}
}
public
class
Role
:
BaseModel<int>
{
public
virtual
IEnumerable<User>
Users
{
get;
set;
}
}
You can notice that all these 3 classes inherits from BaseModel
class. This abstract class contains the common attributes among
all the model classes. EF code first will automatically configure
your database according to the definition of these classes. Here is
the implementation for the base model class.
public
abstract
class
BaseModel<T>
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public
virtual
T
Id
{
get;
set;
}
[Required,
StringLength(maximumLength:
250)]
public
virtual
string
Name
{
get;
set;
}
[StringLength(maximumLength:
1000)]
public
virtual
string
Description
{
get;
set;
}
}
The Repository
The implementation of the repository is generic on the method
level. The advantage of this implementation is that you don't need
to create a repository per model however at the same time it gives
you the flexibility of creating customised repository for a selected
model(s) for more specialised operations like complex queries.
Here is the full implementation of the SqlRepository
class.
The db Context
The db context is the EF code first context. Here is the
implementation of the db context
public
class
EFContext
:
DbContext
{
public
EFContext()
:
base("ReposWithUnitOfWorkDB")
{
Database.SetInitializer<EFContext>(new
DBInitializer());
}
public
new
IDbSet<TEntity>
Set<TEntity>()
where
TEntity
:
class
{
return
base.Set<TEntity>();
}
protected
override
void
OnModelCreating(DbModelBuilder
modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention
>();
modelBuilder.Entity<User>();
modelBuilder.Entity<Role>();
modelBuilder.Entity<Team>();
base.OnModelCreating(modelBuilder);
}
}
The name specified in the call to the base constructor will be the
database name. The implementation of OnModelCreating consists
of two parts:
Removing the PluralizingTableNameConvention
will make
sure that the table names match your model classes names.
Making the context aware of the available model classes by
using the Entity<T> method on the modelBuilder object. EF
code first will then figure out what tables and relations to
create based on the model classes.
In the constructor I am calling my DBInitializer
to insert some
data in the database. This class inherits from
CreateDatabaseIfNotExists
class which creates the
database only if it doesn't exists. It also overrides the Seed
method. Here is the definition of the DBInitializer.
In order to use this class we will add it to our Autofac container and
RunAndCommitTransaction();
break;
case
"3":
RunAndRollbackTransaction();
break;
case
"4":
SelectUsersInTeam();
break;
}
}
}
//
Please
view
source
code
for
full
implementation
}
//
Act
Program.SetContainer(container);
Program.Main(null);
//
Assert
unitOfWorkMock.Verify(a
=>
a.StartTransaction(),
Times.Exactly(1));
unitOfWorkMock.Verify(a
=>
a.CommitTransaction(),
Times.Exactly(1));
}
Arrange
In the arrange section of the unit test I am mocking IDbContext,
UnitOfWork and IConsole. I am passing these mocks to
GetMockContainer which will use these mocks to build the Autofac
container.
The setup for IDbContext focuses on mocking the Set<T> method
which will be used by the repository to insert/select entities. No
setup needed for the IUnitOfWork mock.
The setup for the IConsole mock is interesting. The option to
execute RunAndCommitTransaction function is 2, but we want to
mock the second input as well to mimic the exit option which is
number 5. I am setting the value for my Returns method on the
Setup to be the Deque function on a Queue
object. The way
Returns function works is that it returns the last value it is been set
to return however it accepts a function so in our case the function
Deque will be called 2 times to mimic options 2 then option 5.
Here is the definition for GetMockedContainer
private
IContainer
GetMockedContainer(IDbContext
ctx,
IUnitOfWork
uow,
IConsole
console)
{
var
builder
=
new
ContainerBuilder();
builder.RegisterInstance(ctx).As<IDbContext>();
builder.RegisterInstance(uow).As<IUnitOfWork>();
builder.RegisterInstance(new
Mock<IRepository>().Object).As<IRepository>();
builder.RegisterInstance(console).As<IConsole>();
return
builder.Build();
}
Act
The act section in the unit test is where you take an action to
execute the functionality of the part which you want to test. In our
case we are setting the Autofac container to be the mocked
container then we execute the Main method in Program.
Assert
The assert section in the unit test is where you verify the outcome
of your action. In this case we are expecting that the
StartTransaction and CommitTransaction methods on UnitOfWork
object are being called one time each.
Conclusion
In this article I have explained how to implement the repository
pattern with unit of work. Later we added the usage of IoC
container using Autofac then we added unit test to the demo
application. I hope you enjoyed reading this article and hopefully it
added something new to your knowledge.