ASP.NET Core – MongoDB Repository Pattern & Unit Of Work

The implementation of generic repositories and Unit Of Work are complex tasks in MongoDB. The lack of mechanisms in the component hinders its adoption. See how to implement this article.

Unit of Work

Unit Of Work is a standard used to group one or more operations (usually database operations) into a single transaction or “unit of work”, so that all operations are approved or disapproved as one.

Repository Pattern

Repositories are classes or components that encapsulate the logic needed to access the database (repository). Centralize common data access functionality. Providing better maintenance and decoupling of the infrastructure from the domain layer.

Benefits

Code decoupling and re-usability.

Creating a Repository Pattern with Generics in .NET allows you to implement CRUD operations with very little effort and code.

Together with the Unit Of Work, in case of any error in the flow of the operation, it avoids any modification in the bank.

And this control is in the application rather than opening transactions in the bank and avoiding locks in the tables.

Where’s MongoDB?

MongoDB does not have transaction control in the .NET driver. With MongoDB’s popularity increasing, more and more systems are being implemented with it. Soon this article will address how to implement one.

Implementation

For this implementation, you will need the MongoDB.Driver and ServiceStack.Core components.

To install the components, open the Package Manager (View> Other Windows> Package Manager Console) and enter the commands:

Install-Package ServiceStack.Core
Install-Package MongoDB.Driver

Generic Repository

To implement the Repository Pattern, an abstract generics class will be used with CRUD operations.

Interface

public interface IRepository<TEntity> : IDisposable where TEntity : class
{
Task Add(TEntity obj);
Task<TEntity> GetById(Guid id);
Task<IEnumerable<TEntity>> GetAll();
Task Update(TEntity obj);
Task Remove(Guid id);
}

Implementation

public abstract class BaseRepository < TEntity >: IRepository < TEntity > where TEntity : class
{
protected readonly IMongoContext _context ;
protected readonly IMongoCollection < TEntity > DbSet ;

protected BaseRepository ( IMongoContext context )
{
_context = context ;
DbSet = _context . GetCollection < TEntity > ( typeof ( TEntity ). Name );
}

public virtual Task Add ( TEntity obj )
{
return _context . AddCommand ( async () => await DbSet . InsertOneAsync ( obj ));
}

public virtual async Task < TEntity > GetById ( Guid id )
{
var data = await DbSet . FindAsync ( Builders < TEntity >. Filter . Eq ( " _id " , id ));
return data . FirstOrDefault ();
}

public virtual async Task < IEnumerable < TEntity >> GetAll ()
{
var all = await DbSet . FindAsync ( Builders < TEntity >. Filter . Empty );
return all . ToList ();
}

public virtual Task Update ( TEntity obj )
{
return _context . AddCommand ( async () =>
{
await DbSet . ReplaceOneAsync ( Builders < TEntity >. Filter . Eq ( " _id " , obj . GetId ()) obj );
});
}

public virtual Task Remove ( Guid id ) => _context . AddCommand (() => DbSet . DeleteOneAsync ( Builders < TEntity >. Filter . Eq ( " _id " , id )));

public void Dispose ()
{
GC . SuppressFinalize ( this );
}
}

Each Model will have its own implementation. For example a Product class:

public interface IProductRepository : IRepository < Product >
{
}

// Implementation
public class ProductRepository : BaseRepository < Product >, IProductRepository
{
public ProductRepository ( IMongoContext context ): base ( context )
{
}
}

Unit of Work

Unit Of Work will be responsible for performing the transactions that the Repositories have made. For this work to be done, a Mongo Context must be created. This Context will be the connection between the Repository and UoW.

Mongo Context

public class MongoContext : IMongoContext
{
private IMongoDatabase Database { get; set; }
private readonly List<Func<Task>> _commands;
public MongoContext(IConfiguration configuration)
{
// Set Guid to CSharp style (with dash -)
BsonDefaults.GuidRepresentation = GuidRepresentation.CSharpLegacy;

// Every command will be stored and it'll be processed at SaveChanges
_commands = new List<Func<Task>>();

RegisterConventions();

// Configure mongo (You can inject the config, just to simplify)
var mongoClient = new MongoClient(configuration.GetSection("MongoSettings").GetSection("Connection").Value);

Database = mongoClient.GetDatabase(configuration.GetSection("MongoSettings").GetSection("DatabaseName").Value);
}

private void RegisterConventions()
{
var pack = new ConventionPack
{
new IgnoreExtraElementsConvention(true),
new IgnoreIfDefaultConvention(true)
};
ConventionRegistry.Register("My Solution Conventions", pack, t => true);
}

public int SaveChanges()
{
var qtd = _commands.Count;
foreach (var command in _commands)
{
command();
}

_commands.Clear();
return qtd;
}

public IMongoCollection<T> GetCollection<T>(string name)
{
return Database.GetCollection<T>(name);
}

public void Dispose()
{
GC.SuppressFinalize(this);
}

public Task AddCommand(Func<Task> func)
{
_commands.Add(func);
return Task.CompletedTask;
}
}

UoW implementation

public interface IUnitOfWork : IDisposable
{
bool Commit ();
}

public class UnitOfWork : IUnitOfWork
{
private readonly IMongoContext _context ;

public UnitOfWork ( IMongoContext context )
{
_context = context ;
}

public bool Commit ()
{
return _context . SaveChanges () > 0 ;
}

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

Configuring Startup.cs

To finalize the configuration, open the project’s Startup.cs and add the DI settings.

public void ConfigureServices ( IServiceCollection services )
{
services . AddMvc (). SetCompatibilityVersion ( CompatibilityVersion . Version_2_2 );
services . AddScoped < IMongoContext , MongoContext > ();
services . AddScoped < IUnitOfWork , UnitOfWork > ();
services . AddScoped < IProductRepository , ProductRepository > ();
}

The project code is available on Bruno Brito’s GitHub

I have then the post by Bruno Brito and translated it in to English, mainly because the article was simple and what I was working on

Repository Pattern – for the REST API

The Repository Pattern used to be the next big thing when it was used, but over time it got replaced by frameworks such as the Entity Framework and LINQ, which provided much more functionality and flexibility.

I started working on an external customers REST API then I realised that the Repository Pattern would work perfectly here.

Let’s recap the Repository Pattern.

The Repository Pattern has gained quite a bit of popularity since it was first introduced as a part of Domain-Driven Design in 2004. Primarily, it provides an abstraction of data, so that your application can work with a pure abstraction that has an interface approximating that of a collection. Adding, removing, updating, and selecting items from this collection is done through a series of straightforward methods, without the need to deal with database concerns like connections, commands, cursors, or readers. Using this pattern can help achieve loose coupling and can keep domain objects persistence ignorant. Although the pattern is prevalent (or perhaps because of this), it is also frequently misunderstood and misused. There are many different ways to implement the Repository pattern. Let’s consider a few of them, and their merits and drawbacks.

Repository Per Entity or Business Object

The most straightforward approach, especially with an existing system, is to create a new Repository implementation for each business object you need to store to or retrieve from your persistence layer. Further, you should only implement the specific methods you are calling in your application. Avoid the trap of creating a “standard” repository class, base class, or default interface that you must implement for all repositories. Yes, if you need to have an Update or a Delete method, you should strive to make its interface consistent (does Delete take an ID, or does it take the object itself?). Don’t implement a Delete method on your LookupTableRepository that you’re only ever going to be calling List(). The most significant benefit of this approach is YAGNI – you won’t waste any time implementing methods that never get called.

Generic Repository Interface

Another approach is to go ahead and create a simple, generic interface for your Repository. You can constrain what kind of types it works with to be of a specific type or to implement a particular interface (e.g. ensuring it has an Id property, as is done below using a base class). An example of a generic C# repository interface might be:

public interface IRepository<T> where T : EntityBase
{
    T GetById(int id);
    IEnumerable<T> List();
    IEnumerable<T> List(Expression<Func<T, bool>> predicate);
    void Add(T entity);
    void Delete(T entity);
    void Edit(T entity);
}
 
public abstract class EntityBase
{
   public int Id { get; protected set; }
}

The advantage of this approach is that it ensures you have a common interface for working with any of your objects. You can also simplify the implementation by using a Generic Repository Implementation (below). Note that taking in a predicate eliminates the need to return an IQueryable since any filter details can be passed into the repository. This can still lead to leaking of data access details into calling code, though. Consider using the Specification pattern (described below) to alleviate this issue if you encounter it.

Generic Repository Implementation

Assuming you create a Generic Repository Interface, you can implement the interface generically as well. Once this is done, you can quickly develop repositories for any given type without having to write any new code, and your classes the declare dependencies can merely specify IRepository<Item> as the type, and it’s easy for your IoC container to match that up with a Repository<Item> implementation. You can see an example Generic Repository Implementation, using Entity Framework, here.

public class Repository<T> : IRepository<T> where T : EntityBase
{
private readonly ApplicationDbContext _dbContext;

public Repository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

public virtual T GetById(int id)
{
return _dbContext.Set<T>().Find(id);
}

public virtual IEnumerable<T> List()
{
return _dbContext.Set<T>().AsEnumerable();
}

public virtual IEnumerable<T> List(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
return _dbContext.Set<T>()
.Where(predicate)
.AsEnumerable();
}

public void Insert(T entity)
{
_dbContext.Set<T>().Add(entity);
_dbContext.SaveChanges();
}

public void Update(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
}

public void Delete(T entity)
{
_dbContext.Set<T>().Remove(entity);
_dbContext.SaveChanges();
}
}

Note that in this implementation, all operations are saved as they are performed; there is no Unit of Work being applied. There are a variety of ways in which Unit of Work behaviour can be added to this implementation, the simplest of which being to add an explicit Save() method to the IRepository<T> method, and to only call the underlying SaveChanges() method from this method.

IQueryable?

Another common question with Repositories has to do with what they return. Should they return data, or should they return queries that can be further refined before execution (IQueryable)? The former is safer, but the latter offers a great deal of flexibility. In fact, you can simplify your interface only to provide a single method for reading data if you go the IQueryable route since from there any number of items can be returned.

A problem with this approach is that it tends to result in business logic bleeding into higher application layers and becoming duplicated there. If the rule for returning valid customers is that they’re not disabled and they’ve bought something in the last year, it would be better to have a method ListValidCustomers() that encapsulates this logic rather than specifying these criteria in lambda expressions in multiple different UI layer references to the repository. Another typical example in real applications is the use of “soft deletes” represented by an IsActive or IsDeleted property on an entity. Once an item has been deleted, 99% of the time it should be excluded from display in any UI scenario, so nearly every request will include something like

.Where(foo => foo.IsActive)

in addition to whatever other filters are present. This is better achieved within the repository, where it can be the default behaviour of the List() method, or the List() method might be renamed to something like ListActive(). If it’s essential to view deleted/inactive items, a unique List method can be used for just this (probably rare) purpose.

Specification

Repositories that follow the advice of not exposing IQueryable can often become bloated with many custom query methods. The solution to this is to separate queries into their types, using the Specification Design Pattern. The Specification can include the expression used to filter the query, any parameters associated with this expression, as well as how much data the query should return (i.e. “.Include()” in EF/EF Core). Combining the Repository and Specification patterns can be a great way to ensure you follow the Single Responsibility Principle in your data access code. See an example of how to implement a generic repository along with a generic specification in C#.

Repository Pattern for the REST API

Now let’s see if this can work for the REST API, first the HTTP verbs that are used, the primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are POST, GET, PUT, PATCH, and DELETE. These correspond to create, read, update, and delete (or CRUD) operations, respectively. There are a number of other verbs, too, but are utilized less frequently. Of those less-frequent methods, OPTIONS and HEAD are used more often than others.

HTTP Verb CRUD
POST Create
GET Read
PUT Update/Replace
PATCH Update/Modify
DELETE Delete

One of the main difference is that the calls to REST API are Asynchronous, and the interface needs to reflect this.

public interface IRepository
 {
    Task AddAsync<T>(T entity, string requestUri);
    Task<HttpStatusCode> DeleteAsync(string requestUri);
    Task EditAsync<T>(T t, string requestUri);
    Task<T> GetAsync<T>(string path);
 }

In the concrete implementation I am using the HttpClient to connect to the REST API, which needs a few parameters:

  • Uri of the end point
  • Authorization – type of authorization to be used, default NoAuthHttpSample
  • username – username for basic authorization – default null
  • password – the password for basic authorization – default null

A sample application can be found here:

Original reference Repository Pattern A data persistence abstraction