Quick start: ASP.Net Core 3.1, Entity Framework Core, CQRS, React JS Series — Part 4: Repository pattern implementation

Ali Süleyman TOPUZ
.Net Programming
Published in
4 min readDec 29, 2020
Well organized pieces make it understandable and readable!

In this content, repository pattern will be implemented on entity framework db context.

The parent content of this series: Quick start: ASP.Net Core 3.1, Entity Framework Core, CQRS, React JS Series

Outline

  • Github feature branch
  • Project structure
  • Repository pattern implementation (Base and Wrapper)
  • Definition of domain based repositories
  • What is CQRS?
  • Implementing CQRS with MediatR library
  • Extensions
  • Startup
  • Use it in API
  • Respone

Github feature branch

Project Structure

Repository folder in Domain and Infrastructure.EF projects are placed.

Repository pattern implementation (Base, Wrapper)

Base definiton and Wrapper are separated.

public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
{
protected RepositoryContext RepositoryContext { get; set; }
public RepositoryBase(RepositoryContext repositoryContext)
{
this.RepositoryContext = repositoryContext;
}
public IQueryable<T> FindAll()
{
return this.RepositoryContext.Set<T>().AsNoTracking();
}
public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
{
return this.RepositoryContext.Set<T>()
.Where(expression).AsNoTracking();
}
public void Create(T entity)
{
this.RepositoryContext.Set<T>().Add(entity);
}
public void Update(T entity)
{
this.RepositoryContext.Set<T>().Update(entity);
}
public void Delete(T entity)
{
this.RepositoryContext.Set<T>().Remove(entity);
}
}
public class RepositoryWrapper : IRepositoryWrapper
{
private RepositoryContext _repoContext;
public RepositoryWrapper(RepositoryContext repositoryContext)
{
_repoContext = repositoryContext;
}

private IProductRepository _products;

public IProductRepository Product
{
get
{
if (_products == null)
{
_products = new ProductRepository(_repoContext);
}
return _products;

}
}

private ITagRepository _tags;
public ITagRepository Tag
{
get
{
if (_tags == null)
{
_tags = new TagRepository(_repoContext);
}
return _tags;
}
}

private IProductsTagsRepository _productsInTags;

public IProductsTagsRepository ProductsTags
{
get
{
if (_productsInTags == null)
{
_productsInTags = new Repositories.ProductsTagsRepository(_repoContext);
}
return _productsInTags;
}
}

public async Task SaveAsync()
{
await _repoContext.SaveChangesAsync();
}
}

Definition of domain based repositories

Async GET methods are in placed for domain based repositories

public interface IProductRepository : IRepositoryBase<Product>
{
Task<IEnumerable<Product>> GetAllProductsAsync();
Task<Product> GetProductByIdAsync(Guid productId);
Task<Product> GetProductWithDetailsAsync(Guid productId);
void CreateProduct(Product product);
void UpdateProduct(Product product);
void DeleteProduct(Product product);
}

Implementation:

public class ProductRepository : RepositoryBase<Product>, IProductRepository
{
public ProductRepository(RepositoryContext repositoryContext)
: base(repositoryContext)
{
}

public void CreateProduct(Product product)
{
Create(product);
}

public void DeleteProduct(Product product)
{
Delete(product);
}

public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
return await FindAll()
.OrderBy(p => p.Name)
.ToListAsync();
}

public async Task<Product> GetProductByIdAsync(Guid productId)
{
return await FindByCondition(product => product.Id.Equals(productId))
.FirstOrDefaultAsync();
}

public async Task<Product> GetProductWithDetailsAsync(Guid productId)
{
return await FindByCondition(product => product.Id.Equals(productId))
.Include(ac => ac.ProductsTags)
.ThenInclude(x => x.Tag)
.FirstOrDefaultAsync();
}

public void UpdateProduct(Product product)
{
Update(product);
}
}

What is CQRS?

Command–query separation is a principle of imperative computer programming. It was devised by Bertrand Meyer as part of his pioneering work on the Eiffel programming language. It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both.

Implementing CQRS with MediatR library

In Domain.Services project

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

Define Query:

namespace Domain.Services
{
public class GetProductByIdQuery : IRequest<Product>
{
public Guid Id { get; set; }
}
}

Define Query Handler:

public class GetProductByIdQueryHandler : IRequestHandler<GetProductByIdQuery, Product>
{
public ILoggerManager Logger;
public IRepositoryWrapper Repository { get; }
public GetProductByIdQueryHandler(ILoggerManager logger, IRepositoryWrapper repository)
{
Repository = repository;
Logger = logger;
}
public async Task<Product> Handle(GetProductByIdQuery request, CancellationToken cancellationToken)
{
var tag = await Repository.Product.GetProductByIdAsync(request.Id);
Logger.LogInfo($"product returned from database.");
return tag;
}
}

That’s it!

Dependency Injection for your queries, commands and handlers:

public static class DomainServicesExtensions
{
public static void ConfigureMediatR(this IServiceCollection services)
{
services.AddMediatR(typeof(GetAllTagsQueryHandler));
}
}

Startup

public class Startup
{
public Startup(IConfiguration configuration)
{
LoggerServiceExtensions.LoadConfiguration();
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureCors();
services.ConfigureIISIntegration();
services.ConfigureLoggerService();
services.ConfigureDbContext(Configuration);
services.ConfigureRepositoryWrapper();
services.ConfigureAutoMapper();
services.ConfigureValidationFilter();
services.ConfigureMediatR();
services.AddControllers().AddNewtonsoftJson(o => o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
services.ConfigureSwagger();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.ConfigureCustomExceptionMiddleware();
app.ConfigureSwaggerMiddleware();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("CorsPolicy");
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.All
});
app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}

Use it in API:

[HttpGet("{id}", Name = "ProductById")]
public async Task<IActionResult> GetProductById(Guid id)
{
var products = await _mediator.Send(new GetProductByIdQuery() { Id = id });
var productsResult = _mapper.Map<ProductDto>(products);
return Ok(productsResult);
}

Response

Conclusion

In this content, Repository implementation, CQRS and API implementation are demonstrated.

--

--

Ali Süleyman TOPUZ
.Net Programming

Software Engineering and Development Professional. Writes about software development & tech. 📍🇹🇷