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.
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:
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
public interface IBrokerContext :IDisposable
{
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")
{
Database = new MyDatabase(base.Database);
Database.SetInitializer(new CreateDatabaseIfNotExists<BrokerContext>());
}
.
.
.
public new IMyDatabase Database { get; }
}
Second Level
public interface IMyDatabase
{
int ExecuteSqlCommand(string sql, params object[] parameters);
void SetInitializer<TContext>
(IDatabaseInitializer<TContext> strategy) where TContext : DbContext;
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()
{
var myDbContextTransaction = new MyDbContextTransaction(_database.BeginTransaction());
return myDbContextTransaction;
}
}
Third Level
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.