A repository pattern implementation using ccqrs concepts.
Often applications need to access a database to store and query information. The Repository Pattern serves as an abstraction to persist data, reducing code duplication and increasing cohesion and low coupling between parts of the system.
Command Query Responsibility Segregation (CQRS) is an architectural standard that seeks to separate the responsibility for writing and reading your data.
This project provides a generic Repository interface using the CQRS concepts.
Use the abstract class ReadRepository<TDbContext, TEntity>
where TDbContext
is a DbContext
, and TEntity
is a class, to perform queries and retrieve the data.
You can inherit it in your own class, or use extension methods.
var dbContextOptions = new DbContextOptionsBuilder<BookStoreContext>()
.UseInMemoryDatabase(databaseName: "BookStoreContext")
.Options;
var bookStoreContext = new BookStoreContext(dbContextOptions);
var bookReadRepository = new DefaultReadRepository<BookStoreContext, Book>(bookStoreContext);
var book = bookReadRepository.FindById(1);
For complex and custom queries, you can use the QueryOptions class to specify your query.
var queryOptions = new QueryOptions<Author>
{
Where = a => a.Books.Any(),
Sort = new SortOptions<Author>
{
OrderBy = a => a.FirstName
},
Includes = new Expression<Func<Author, object>>[]
{
a => a.Books
}
};
IQuery query = authorReadRepository.Query(queryOptions);
var firstAuthor = await query.FirstAsync();
var lastAuthor = await query.LastAsync();
var filteredAuthors await query.ToListAsync();
You can use the
QueryBuilder
class to construct the query specification.
var queryOptions = QueryBuilder.For<Author>()
.Where(a => a.Books.Count() == 3)
.Include(a => a.Books)
.OrderBy(a => a.FirstName)
.ThenBy(a => a.LastName)
.Descending()
.Build();
var authors = authorReadRepository.Find(queryOptions);
The
MosaicSolutions.GenericRepository.Repositories.Read
namespace only contains interfaces for query operations, that is, they do not change the state of the database.
Use the WriteRepository<TDbContext, TEntity>
class, where TDbContext
is a DbContext
and TEntity
is a class, to perform operations that change the state of the data.
You can inherit it in your own class, or use extension methods.
var dbContextOptions = new DbContextOptionsBuilder<BookStoreContext>()
.UseInMemoryDatabase(databaseName: "BookStoreContext")
.Options;
var bookStoreContext = new BookStoreContext(dbContextOptions);
var authorWriteRepository = new DefaultWriteRepository<BookStoreContext, Author>(bookStoreContext);
var unitOfWork = new UnitOfWork<BookStoreContext>(bookStoreContext);
var newAuthor = new Author
{
FirstName = "William",
LastName = "Shakespeare"
};
authorWriteRepository.Insert(newAuthor);
var rowsAffected = unitOfWork.Commit();
All methods of the
WriteRepository<TDbContext, TEntity>
class do not callSaveChanges()
in their execution. Use theUnitOfWork
pattern by calling theCommit()
method to save the changes.
If you need to perform a task in a transaction context, the namespace MosaicoSolutions.GenericRepository.Repositories.Write.Transactions
supports transactions.
var dbContextOptions = new DbContextOptionsBuilder<BookStoreContext>()
.UseInMemoryDatabase(databaseName: "BookStoreContext")
.Options;
var transactionTaskManager = new TransactionTaskManager<BookStoreContext>(() => new new BookStoreContext(dbContextOptions));
var authorTransactionalRepository = new TransactionalRepository<Author>();
var newAuthor = new Author
{
FirstName = "William",
LastName = "Shakespeare"
};
var insertTask = authorTransactionalRepository.InsertAsTransactionTask(newAuthor);
var transanctionTaskResult = transactionTaskManager.UseTransaction(insertTask);
if (transanctionTaskResult.Success)
{
Console.WriteLine("Author inserted successfully!");
}
else
{
Console.WriteLine("Error while trying to insert a new author.");
Console.WriteLine($"Erro Message: {transanctionTaskResult.Exception}");
}
The TransactionTaskManager<TDbContext>
class is used to perform an operation in transaction context. The UseTransaction
method returns a TransactionTaskResult
that tells you whether there was success or failure to perform the operation.
The InsertAsTransactionTask
method returns a TransactionTask
that represents a task that will be executed in the transaction context.
You can check the tests here