Anda di halaman 1dari 17

Repository with Unit of Work,

IoC and Unit Test

Introduction

This article discusses the usage of repository pattern with


unit of work. Then it shows how this pattern can be used with IoC
and unit test.
I will use the repository pattern with unit of work to implement a
data layer for my demo application. I am using EF6 code first to
create a database with 3 tables: Team, Role and User. The user
has a role and she is a member of a team.
The target audience for this tutorial is intermediate to advanced
level .NET developers. It also require prior knowledge of EF code
first.

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.

Using the code


Download the project associated with this article and open it in
Visual Studio. You will need Sql Express installed locally on your
machine to run the application. Once you run the application you
will be introduced with a menu which lists the functionality that the
application can do.
The application it self is a demo on how Repository and Unit of
Work patterns work with each other, as such it is the
implementation of these patterns which matters and not the
application it self.
As the names of the zip folders suggested you can see that each
one refer to a section of the implementation. The Demo_initial
contains the initial implementation. Demo_WithAutofac contains
the same implementation but with the usage of Autofac IoC
container. Demo_WithUnitTest contains the unit test which will be
added at the end with the changes required.

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 data layer


The data layer consists of the Repository, the Unit of Work and the
EF Data Context

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.

public class SqlRepository : IDisposable


{
private readonly DbContext context;

public SqlRepository(DbContext context)
{
this.context = context;
}

public IQueryable<TEntity> GetAll<TEntity>() where TEntity
: class
{
return GetEntities<TEntity>().AsQueryable();
}

public void Insert<TEntity>(TEntity entity) where TEntity
: class
{
GetEntities<TEntity>().Add(entity);
}

public void Delete<TEntity>(TEntity entity) where TEntity
: class
{
GetEntities<TEntity>().Remove(entity);
}

private IDbSet<TEntity> GetEntities<TEntity>() where
TEntity : class
{
return this.context.Set<TEntity>();
}

public void SaveChanges()
{
this.context.SaveChanges();
}

public void Dispose()
{
if (this.context != null)
{
this.context.Dispose();
}
}
}

The implementation is simple enough to get you working with your


model. The main method here is the private method GetEntities
which returns a db set of all the entities from the db context for a
given model.
Based on this generic repository I have created a Team repository
which includes specific queries related to Team model.
public class TeamRepository : SqlRepository
{
public TeamRepository(DbContext context) : base(context)
{}

public List<User> GetUsersInTeam(int teamId)
{
return (from u in this.GetAll<User>()
where u.TeamId == teamId
select u).ToList();
}
}

The TeamRepository benefits from the generic methods in the


parent class but it implements its own specific method.

The Unit of Work


The UnitOfWork class abstracts the use of transaction for the
upper layers. It exposes the needed methods to do just that. Here
is the implementation of this class.
public class UnitOfWork : IDisposable
{
private TransactionScope transaction;

public void StartTransaction()
{
this.transaction = new TransactionScope();
}

public void CommitTransaction()
{
this.transaction.Complete();
}

public void Dispose()
{
this.transaction.Dispose();
}
}

You will see the usage of this class shortly.

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.

public class DBInitializer :


CreateDatabaseIfNotExists<EFContext>
{
protected override void Seed(EFContext context)
{
using (var ctx = new EFContext())
{
// Please see source code for implementation
}
}
}

The user interface


The user interface is a console application with some options to
see the repository and unit of work patterns in action.

The first option will trigger the DBInitializer to create the


database if it doesn't exist, so it is advisable to select this option
when you run the application for the first time.
Here is the implementation for option 2.
Copy Code

private static void RunAndCommitTransaction()


{
using (var uof = new UnitOfWork())
{
uof.StartTransaction();
var repo = new SqlRepository(new EFContext());

Console.WriteLine("\nStarting a tranaction....");

var role = new Role { Name = "Tester", Description =
"Tester description" };
repo.Insert<Role>(role);

var user = new User { Name = "Andy", Description =
"Andy user description", email = "Andy@email.com", Password =
"123" };
repo.Insert<User>(user);
user.Role = role;

user.Team = repo.GetAll<Team>().FirstOrDefault(t =>


t.Name.Equals("Los Banditos"));

repo.SaveChanges();
uof.CommitTransaction();

Console.WriteLine(string.Format("\nThe tranaction has
been commited.\nUser '{0}' and Role '{1}' were added
successfully", user.Name, role.Name));
}
}

In this method I am making use of the unit of work to put my


operations into one transaction. I am also using the generic
methods in the SqlRepository to insert and retrieve entities.
Option number 3 has almost the same content as option 2 but
instead of committing the transaction I am rolling it back. The roll
back will happen simply by not calling the commit transaction or if
an exception is thrown before you call commit transaction.
Option 4 make use of the TeamRepository to get all the users in
a given team. Here is the implementation for option 4.
private static void SelectUsersInTeam()
{
using (var ctx = new EFContext())
{
var repo = new TeamRepository(ctx);
var teams = repo.GetAll<Team>().ToList();

teams.ForEach(t => Console.WriteLine(string.Format("Team
Name:{0}, Team Id: {1}", t.Name, t.Id)));

Console.Write("Enter team id: ");
var teamId = Console.ReadLine();

repo.GetUsersInTeam(int.Parse(teamId)).ForEach(
u => Console.WriteLine(string.Format("Name: {0}, Email:
{1}", u.Name, u.email))
);
}
}

Building IoC Container Using


Autofac
Autofac will help to resolve all dependencies required by the
application. It enhances decoupling between different application
components. The idea of an IoC container is that it allows you to
define all your code dependencies in a central location (container)
and allows you to access them upon need.
I have created a BootStrap class to my application. This class
has one method called BuildContainer which returns the Autofac
container. Here is the implementation for BootStrap
public class BootStrap
{
public static IContainer BuildContainer()
{
var builder = new ContainerBuilder();

builder.RegisterType<EFContext>().As<IDbContext>();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>();

builder.RegisterType<SqlRepository>().As<IRepository>();

builder.RegisterType<TeamRepository>().As<ITeamRepository>();

return builder.Build();
}
}

First I am creating the ContainerBuilder then I am registering


my types. Note that I am registering my implementation types
using the interfaces which they implement.
This will allow you to ask the container to resolve an Interface
implementation rather than the implementation itself. This
enhances decoupling as you are no longer dependent on a
specific implementation but any class which implements a given
interface. This concept is the heart of IoC because whoever is
using the interface doesn't need to know which class implements it
as long as it implements that interface.
This feature allows you to swap the implementation for a given
class in one location only which is when building the container.

Defining the Interfaces


I have extracted interfaces for the data layer components which I
have defined earlier, Here is the definition for these interfaces.
public interface IDbContext : IDisposable
{
IDbSet<TEntity> Set<TEntity>() where TEntity : class;
int SaveChanges();
}

public interface IRepository : IDisposable
{
void Delete<TEntity>(TEntity entity) where TEntity :
class;
IQueryable<TEntity> GetAll<TEntity>() where TEntity :
class;
void Insert<TEntity>(TEntity entity) where TEntity :
class;
void SaveChanges();
}

public interface ITeamRepository : IRepository
{
List<ReposWithUnitOfWorkSol.Model.User> GetUsersInTeam(int
teamId);
}

public interface IUnitOfWork : IDisposable
{
void CommitTransaction();
void StartTransaction();
}

Note that some of these interfaces inherits from IDisposable


interface. IDisposable will be implemented by the class which
implements this interface for example UnitOfWork and
SqlRepository. This is very important as we are dealing with
external resources which needs disposing once finished working
with, namely DbContext and TransactionScope.

Using the Autofac IoC


container
You can resolve dependencies from the Autofac container by:
Injecting your dependencies into your constructor and let Autofac
resolve them
Make an instance of your container available and resolve your
dependencies once you need them
In the demo application I am using both approaches.

Injecting the Dependencies


The SqlRepository class is making use of this feature. Here is the
new implementation of this class which was defined earlier.
public class SqlRepository : IRepository
{
private readonly IDbContext context;

public SqlRepository(IDbContext context)
{
this.context = context;
}
}
// Please refer to source code for full implementation

The SqlRepository now implements the IRepository interface.


Notice the IDbContext interface in the constructor. When you try to
resolve SqlRepository from the Autofac container, Autofac will
automatically inject this dependency as long as you register the
dependency i.e. IDbContext which we are doing in our BootStrap
shown earlier.

Resolve Dependencies on Demand


This means that you will not inject dependencies in the constructor
but ask for a given dependency when you want to use it.
Here is an example for this usage from
RunAndRollbackTransaction method.

private static void RunAndRollbackTransaction()


{
using (var uof = container.Resolve<IUnitOfWork>())
using (var repo = container.Resolve<IRepository>())
{
uof.StartTransaction();

Console.WriteLine("\nStarting a tranaction....");

var role = new Role { Name = "ProductOwner",
Description = "Product Owner role description" };
repo.Insert<Role>(role);

var user = new User { Name = "Mark", Description =
"Mark user description", email = "Mark@email.com", Password =
"123" };
repo.Insert<User>(user);
user.Role = role;
user.Team = repo.GetAll<Team>().FirstOrDefault(t =>
t.Name.Equals("Los Banditos"));

Console.WriteLine("\nSaving changes....");
repo.SaveChanges();

Console.WriteLine("\nRolling back the transaction....");
Console.WriteLine(string.Format("\nThe tranaction has been
rolled back"));
}
}

The container object is defined in the Main method. It holds the


Autofac container which BootStrap class returns from it's
BuildContainer method. Note the usage of using statement. This is
good practise to dispose any resources once the using block is
executed.
As you can see the implementation is same as the one defined
earlier with on exception which is the resolving of dependencies
from Autoface container.
This method make use of the UnitOfWork class to force the
transaction to rollback and hence the User created in the method
will never be added because we didn't call the CommitTransaction
method on the UnitOfWork object. For the implementation of a
commited transaction please look at the implementation of
RunAndCommitTransaction in the source code.

Prepare for Unit Test


Ideally you will have your tests written first if you are using Test
Driven Development (TDD) however I intended to leave this at the
end so you can see how we moved from the rigid implementation
to then to using Autofac which sets the scene nicely for our unit
test.
To have our Program class unit tested we have to add some
changes to it. Among these changes is a function which can set
the IoC container. This is required because we need to build our
container but instead of adding actual implementation we will be
adding mocks to our classes using moq library.
As our methods are private then we will not be able to directly test
them but what we should do is test a certain scenario which will
lead to our method. This means that we need to mock our way
until we reach our method. As you may see that the UI uses the
Console class to output text and read user input to run the
required option. We need to add an interface which then we can
mock for Console.
Here is the interface and implementation for IConsole
public interface IConsole
{
string ReadInput();
void WriteOutputOnNewLine(string output);
void WriteOutput(string output);
}

public class ConsoleReadWrite : IConsole
{
public string ReadInput()
{
return Console.ReadLine();
}

public void WriteOutput(string output)
{
Console.Write(output);
}

public void WriteOutputOnNewLine(string output)
{
Console.WriteLine(output);
}
}

In order to use this class we will add it to our Autofac container and

resolve it in Main method of our Program. Here is a partial


implementation of Program class which highlights the changes.
Please note that this is not the full implementation. For full
implementation for Program class please refer to source code.
public class Program
{
private static IContainer container;
private static IConsole console;

public static void SetContainer(IContainer
mockedContainer)
{
container = mockedContainer;
}

public static void Main(string[] args)
{
if (container == null)
{
container = BootStrap.BuildContainer();
}

console = container.Resolve<IConsole>();

var userChoice = string.Empty;

while (userChoice != "5")
{
console.WriteOutputOnNewLine("\nChoose one of the
following options by entering option's number:\n ");
console.WriteOutputOnNewLine("1- Initialize
DB\n");
console.WriteOutputOnNewLine("2- Run and commit a
transaction\n");
console.WriteOutputOnNewLine("3- Run and rollback
a transaction\n");
console.WriteOutputOnNewLine("4- Select users in a
team\n");
console.WriteOutputOnNewLine("5- Exit");

userChoice = console.ReadInput();

switch (userChoice)
{
case "1":
InitializeDB();
break;
case "2":

RunAndCommitTransaction();
break;
case "3":
RunAndRollbackTransaction();
break;
case "4":
SelectUsersInTeam();
break;
}
}
}
// Please view source code for full implementation
}

In the Main method I am checking to see whether the Autofac


container is set or not. This check will allow me set my Autofac
container from the unit test as you will see shortly.

The unit test


Now we have all we need to start writing our unit test. The method
which I will be unit testing is: RunAndCommitTransaction. This unit
test will test that we are actually calling the StartTransaction and
CommitTransaction on our UnitOfWork class. Here is the definition
for this unit test.
[TestMethod]
public void RunAndCommitTransaction_WithDefault()
{
// Arrange
var contextMock = new Mock<IDbContext>();
contextMock.Setup(a =>
a.Set<User>()).Returns(Mock.Of<IDbSet<User>>);
contextMock.Setup(a =>
a.Set<Role>()).Returns(Mock.Of<IDbSet<Role>>);
contextMock.Setup(a =>
a.Set<Team>()).Returns(Mock.Of<IDbSet<Team>>);

var unitOfWorkMock = new Mock<IUnitOfWork>();

var consoleMock = new Mock<IConsole>();
consoleMock.Setup(c => c.ReadInput()).Returns(new
Queue<string>(new[] { "2", "5" }).Dequeue);

var container = GetMockedContainer(contextMock.Object,
unitOfWorkMock.Object, consoleMock.Object);

// 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();
}

As the IRepository mock doesn't need any setup, I am just getting


the Object from the Mock rather than passing it in.

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.

Anda mungkin juga menyukai