Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Mocking EF DbContext

4.83/5 (4 votes)
13 Oct 2016CPOL 25.2K  
Adapter implemantation to mock DbContext easily

Introduction

If you want to mock DbContext, these adapters will help you to test your code easily.

Background

Adapter

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

Using the Code

I wanted to mock DbContext. So I implemented an interface for my context class.

C#
public class BrokerContext : DbContext, IBrokerContext
{
.
.
.
}

This was fine, but then I couldn't mock context.Database.ExecuteSqlCommand().

So, I implemented an adapter for Database property.

But later, I had another problem:

C#
using(var myTransaction=context.Database.BeginTransaction())
{
 myTransaction.Commit();
}

This time, I couldn't mock context.Database.BeginTransaction().

So I implemented adapter for DbContextTransaction.

Here is the last code:

Main Level

C#
 public interface IBrokerContext :IDisposable
 {
     //This is our Interface. We can mock Database with this Interface.
     IMyDatabase Database { get; }
     .
     .
     .
     DbContextConfiguration Configuration { get; }
     int SaveChanges();
     DbSet<TEntity> Set<TEntity>() where TEntity : class;
     DbSet Set(Type entityType);
     Task<int> SaveChangesAsync();
     DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
     DbEntityEntry Entry(object entity);
 }

public class BrokerContext : DbContext, IBrokerContext
 {
     public BrokerContext()
         : base("Name=BrokerContext")
     {
         //We initialize our Database(IMyDatabase).
         Database = new MyDatabase(base.Database);
         Database.SetInitializer(new CreateDatabaseIfNotExists<BrokerContext>());
     }
     .
     .
     .
     //We are hiding base Class Database.
     public new IMyDatabase Database { get; }
 }

Second Level

C#
public interface IMyDatabase
 {
     int ExecuteSqlCommand(string sql, params object[] parameters);
     void SetInitializer<TContext>
     (IDatabaseInitializer<TContext> strategy) where TContext : DbContext;
     //This is our Interface. We can mock BeginTransaction.
     IMyDbContextTransaction BeginTransaction();
 }

 public class MyDatabase : IMyDatabase
 {
     private readonly Database _database;

     public MyDatabase(database)
     {
         _database = database;
     }

     public int ExecuteSqlCommand(string sql, params object[] parameters)
     {
        return _database.ExecuteSqlCommand(sql,parameters);
     }

     public void SetInitializer<TContext>
     (IDatabaseInitializer<TContext> strategy) where TContext : DbContext
     {
         Database.SetInitializer(strategy);
     }

     public IMyDbContextTransaction BeginTransaction()
     {
        //Initialize our DbContextTransaction(MyDbContextTransaction)
        var myDbContextTransaction = new MyDbContextTransaction(_database.BeginTransaction());
         return myDbContextTransaction;
     }
 }

Third Level

C#
 public interface IMyDbContextTransaction:IDisposable
    {
        void Commit();
        void Rollback();
    }

 public class MyDbContextTransaction : IMyDbContextTransaction
{
    private readonly DbContextTransaction _dbContextTransaction;

    public MyDbContextTransaction(DbContextTransaction dbContextTransaction)
    {
        _dbContextTransaction = dbContextTransaction;
    }

    public void Commit()
    {
        _dbContextTransaction.Commit();
    }

    public void Rollback()
    {
        _dbContextTransaction.Rollback();
    }

    public void Dispose()
    {
        _dbContextTransaction.Dispose();
    }
}

I added only methods which I need on adapters. Also, I just implemented adapters if I really needed them, not for all DbContext.

Edit

If you don't want to control all DbContext.Database, here is a simpler solution.

License

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