Design Patterns - Unit of Work Pattern in C# using MongoDB (with transactions)
Before you look at the Unit of Work pattern example in C# using MongoDB, you can see my post about what is the Unit of Work pattern.
Unit of Work interface in C#
public interface IUnitOfWork
{
IDisposable Session { get; }
void AddOperation(Action operation);
void CleanOperations();
Task CommitChanges();
}
About this code snippet:
- The property
Session
will be replaced by the MongoDB session object in the implementation of the Unit of Work. - The method
AddOperation
is used to add operations to the transaction. - The method
CleanOperations
clean all the operations (Before the execution of the transaction). - The method
CommitChanges
execute all operations in the transaction.
Implementation of the Unit of Work in C# using MongoDB
public sealed class UnitOfWork : IUnitOfWork
{
private IClientSessionHandle session { get; }
public IDisposable Session => this.session;
private List<Action> _operations { get; set; }
public UnitOfWork()
{
var mongoClient = new MongoClient("connectionString");
this.session = mongoClient.StartSession();
this._operations = new List<Action>();
}
public void AddOperation(Action operation)
{
this._operations.Add(operation);
}
public void CleanOperations()
{
this._operations.Clear();
}
public async Task CommitChanges()
{
this.session.StartTransaction();
this._operations.ForEach(o =>
{
o.Invoke();
});
await this.session.CommitTransactionAsync();
this.CleanOperations();
}
}
About this code snippet:
- The constructor starts a MongoDB session.
- The property
Session
returns the MongoDB session object.
Repository interface in C#
public interface IProductRepository
{
void Add(IProduct product);
void Update(IProduct product);
void Remove(ProductId id);
}
About this code snippet:
- For this example, I am only including the methods that will go inside a transaction.
- These actions are not asynchronous because they are not executed instantly, instead, they just add operations to the transaction.
Implementation of the Repository in C# using MongoDB
public sealed class ProductRepository : IProductRepository
{
private readonly IMongoDatabase _database;
private readonly IMongoCollection<ProductMongoEntity> _productsCollection;
private readonly IUnitOfWork _unitOfWork;
public ProductRepository(IUnitOfWork unitOfWork)
{
this._unitOfWork = unitOfWork;
var mongoClient = new MongoClient("connectionString");
this._database = mongoClient.GetDatabase("databaseName");
this._productsCollection = this._database.GetCollection<ProductMongoEntity>("collectionName");
}
public void Add(IProduct product)
{
Action operation = () => this._productsCollection.InsertOne(this._unitOfWork.Session as IClientSessionHandle, product);
this._unitOfWork.AddOperation(operation);
}
public void Update(IProduct product)
{
Action operation = () => this._productsCollection.ReplaceOne(this._unitOfWork.Session as IClientSessionHandle, x => x.Id == product.Id, product);
this._unitOfWork.AddOperation(operation);
}
public void Remove(ProductId id)
{
Action operation = () => this._productsCollection.DeleteOne(this._unitOfWork.Session as IClientSessionHandle, x => x.Id == id.Id);
this._unitOfWork.AddOperation(operation);
}
}
About this code snippet:
- The Unit of Work is injected into the repository because it is necessary to know the MongoDB session.
- Each method of the Repository creates an action with the data of the operation to perform and adds it to the list of operations of the Unit of Work.