Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C# 4.0

Repository with Unit of Work, IoC and Unit Test

Rate me:
Please Sign up or sign in to vote.
4.98/5 (26 votes)
24 Mar 2014CPOL11 min read 141.7K   7.5K   67   34
Repository with Unit of Work

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

C#
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.

C#
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.

C#
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.

C#
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.

Image 1

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.

C#
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

C#
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.

C#
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.

C#
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.

C#
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

C#
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.

C#
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.

C#
[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

C#
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.

History

22 March 2014: V 1.0: Created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Advanced Legal
United Kingdom United Kingdom
I am a software engineer specialised in the .NET stack (C#). I hold a Master degree in Internet Engineering from the University of Sunderland. I am very passionate about technology and learning new stuff.

Website: sulhome.com

LinkedIn: Sul Aga

Comments and Discussions

 
QuestionUnitTest: Test if a method is called on all Interfaces in a Namesapce Pin
maryam.saboor18-Apr-17 0:54
professionalmaryam.saboor18-Apr-17 0:54 
QuestionI have got a question Pin
Shahin Khorshidnia7-Jun-16 22:55
professionalShahin Khorshidnia7-Jun-16 22:55 
QuestionUncorrupted Unit Test File Here: Pin
superjose_4919-Apr-16 2:30
superjose_4919-Apr-16 2:30 
AnswerRe: Uncorrupted Unit Test File Here: Pin
Sul Aga3-Nov-17 3:10
Sul Aga3-Nov-17 3:10 
QuestionThe download archive is corrupt Pin
vish_iitr7-Jul-15 20:51
vish_iitr7-Jul-15 20:51 
AnswerRe: The download archive is corrupt Pin
superjose_4919-Apr-16 2:32
superjose_4919-Apr-16 2:32 
I know this should be irrelevant by now, but here's the uncorrupted file:

Download the uncorrupted file here:
rebuilt.Demo_WithUnitTest[^]

How did I uncorrupt it?

I opened the file in Winrar, and the folder: &quot;ReposWithUnitOfWorkSol&quot; (The first folder that appears on the file), righ-clicked on it and selected &quot;Repair Archive&quot;. Winrar did its magic and bam, it is working!
nice

AnswerRe: The download archive is corrupt Pin
Sul Aga3-Nov-17 3:03
Sul Aga3-Nov-17 3:03 
GeneralRe: The download archive is corrupt Pin
Ivan Halauko20-May-21 21:08
Ivan Halauko20-May-21 21:08 
QuestionDI Versus Service Locator Pin
Member 1078656927-May-15 5:01
Member 1078656927-May-15 5:01 
SuggestionDon't use the Service Locator Anti-Pattern Pin
MetalKid0072-Mar-15 5:27
MetalKid0072-Mar-15 5:27 
QuestionHow about Eager Loaging (Includes), SaveChangesAsync and UpdateEntity Implementations in the SqlRepository Class Pin
tobiAkinseye20-Jan-15 3:58
tobiAkinseye20-Jan-15 3:58 
AnswerRe: How about Eager Loaging (Includes), SaveChangesAsync and UpdateEntity Implementations in the SqlRepository Class Pin
Sul Aga24-Jan-15 8:48
Sul Aga24-Jan-15 8:48 
GeneralRe: How about Eager Loaging (Includes), SaveChangesAsync and UpdateEntity Implementations in the SqlRepository Class Pin
tobiAkinseye30-Jan-15 1:06
tobiAkinseye30-Jan-15 1:06 
GeneralMy vote of 5 Pin
Rajesh Pillai24-Nov-14 3:52
Rajesh Pillai24-Nov-14 3:52 
GeneralRe: My vote of 5 Pin
Sul Aga22-Dec-14 0:09
Sul Aga22-Dec-14 0:09 
QuestionHigh quality article - but what about implementing in MVC? Pin
DavidEKeller11-Jun-14 22:49
DavidEKeller11-Jun-14 22:49 
AnswerRe: High quality article - but what about implementing in MVC? Pin
Sul Aga2-Aug-14 1:55
Sul Aga2-Aug-14 1:55 
AnswerRe: High quality article - but what about implementing in MVC? Pin
Sul Aga18-Aug-14 17:41
Sul Aga18-Aug-14 17:41 
QuestionHollywood principle Pin
Grzegorz Mrozik7-Apr-14 8:17
Grzegorz Mrozik7-Apr-14 8:17 
AnswerRe: Hollywood principle Pin
Sul Aga7-Apr-14 9:59
Sul Aga7-Apr-14 9:59 
Questionzip downloads Pin
Member 800608928-Mar-14 4:56
Member 800608928-Mar-14 4:56 
AnswerRe: zip downloads Pin
Sul Aga28-Mar-14 8:08
Sul Aga28-Mar-14 8:08 
QuestionDownload source problem Pin
Boudino25-Mar-14 4:34
Boudino25-Mar-14 4:34 
AnswerRe: Download source problem Pin
Sul Aga25-Mar-14 4:56
Sul Aga25-Mar-14 4:56 
GeneralRe: Download source problem Pin
Boudino25-Mar-14 6:03
Boudino25-Mar-14 6:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.