Click here to Skip to main content
15,887,596 members
Articles / Programming Languages / C#
Tip/Trick

Mocking EF DbContext

Rate me:
Please Sign up or sign in to vote.
4.83/5 (4 votes)
13 Oct 2016CPOL 24.5K   12   2
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)


Written By
Software Developer (Senior)
Turkey Turkey
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAnother suggestion Pin
Jan Hansen2-Mar-16 3:31
Jan Hansen2-Mar-16 3:31 
AnswerRe: Another suggestion Pin
MaDOnos2-Mar-16 22:01
MaDOnos2-Mar-16 22:01 

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.