Skip to content

Commit

Permalink
Merge pull request #9 from sj-distributor/repo2
Browse files Browse the repository at this point in the history
Implementing the repository and uow patterns
  • Loading branch information
linwenda authored Feb 3, 2023
2 parents 0750242 + c521599 commit 2f20109
Show file tree
Hide file tree
Showing 23 changed files with 260 additions and 128 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ WilTechs Architecture Solution Template for .NET 6
Using dotnet cli template, install the template:

```
dotnet new -i Wax.Template
dotnet new install Wax.Template
```

Run this command to create the solution:
Expand Down
14 changes: 8 additions & 6 deletions src/Wax.Api/Filters/GlobalExceptionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace Wax.Api.Filters;
public class GlobalExceptionFilter : IExceptionFilter
{
private readonly Serilog.ILogger _logger;
private readonly IWebHostEnvironment _env;

public GlobalExceptionFilter(Serilog.ILogger logger)
public GlobalExceptionFilter(Serilog.ILogger logger, IWebHostEnvironment env)
{
_logger = logger;
_env = env;
}

public void OnException(ExceptionContext context)
Expand Down Expand Up @@ -40,12 +42,12 @@ private void HandleBusinessException(ExceptionContext context)

var problemDetails = new ProblemDetails
{
Status = StatusCodes.Status403Forbidden,
Title = "Business error",
Status = StatusCodes.Status409Conflict,
Title = "A business error occur.",
Detail = context.Exception.Message,
Instance = context.HttpContext.Request.Path
};

//problemDetails.Extensions.Add(new KeyValuePair<string, object>("code", "1234"));
context.Result = new ObjectResult(problemDetails);
}

Expand Down Expand Up @@ -84,8 +86,8 @@ private void HandleInternalServerError(ExceptionContext context)
var problemDetails = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "Internal error",
Detail = "An error occur.Try it again later."
Title = "Internal error.",
Detail = _env.IsDevelopment() ? context.Exception.Message : "An error occur. Try it again later."
};

context.Result = new ObjectResult(problemDetails);
Expand Down
9 changes: 5 additions & 4 deletions src/Wax.Core/ApplicationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
using Microsoft.EntityFrameworkCore;
using Serilog;
using Wax.Core.Data;
using Wax.Core.Data.Repositories;
using Wax.Core.DependencyInjection;
using Wax.Core.Domain;
using Wax.Core.Middlewares.FluentMessageValidator;
using Wax.Core.Middlewares.Logging;
using Wax.Core.Repositories;
using Wax.Core.Services.Identity;
using Module = Autofac.Module;

Expand Down Expand Up @@ -84,9 +83,11 @@ private void RegisterDatabase(ContainerBuilder builder)
}).AsSelf().As<DbContext>()
.InstancePerLifetimeScope();

builder.RegisterGeneric(typeof(EfCoreRepository<>))
.As(typeof(IRepository<>))
builder.RegisterGeneric(typeof(EfCoreBasicRepository<>))
.As(typeof(IBasicRepository<>))
.InstancePerLifetimeScope();

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
}

private void RegisterIdentity(ContainerBuilder builder)
Expand Down
20 changes: 0 additions & 20 deletions src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs

This file was deleted.

6 changes: 0 additions & 6 deletions src/Wax.Core/Domain/Customers/ICustomerRepository.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,36 @@
using Mediator.Net.Contracts;
using Wax.Core.Domain.Customers;
using Wax.Core.Domain.Customers.Exceptions;
using Wax.Core.Repositories;
using Wax.Messages.Commands.Customers;

namespace Wax.Core.Handlers.CommandHandlers.Customers
{
public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
{
private readonly IMapper _mapper;
private readonly ICustomerRepository _repository;
private readonly IUnitOfWork _unitOfWork;

public CreateCustomerCommandHandler(IMapper mapper, ICustomerRepository repository)
public CreateCustomerCommandHandler(IMapper mapper, IUnitOfWork unitOfWork)
{
_mapper = mapper;
_repository = repository;
_unitOfWork = unitOfWork;
}

public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
CancellationToken cancellationToken)
{
if (!await _repository.CheckIsUniqueNameAsync(context.Message.Name, cancellationToken)
.ConfigureAwait(false))
var existing = await _unitOfWork.Customers.FindByNameAsync(context.Message.Name, cancellationToken);

if (existing != null)
{
throw new CustomerNameAlreadyExistsException();
}

var customer = _mapper.Map<Customer>(context.Message);

await _repository.InsertAsync(customer, cancellationToken).ConfigureAwait(false);
await _unitOfWork.Customers.InsertAsync(customer, cancellationToken).ConfigureAwait(false);
await _unitOfWork.SaveChangesAsync(cancellationToken);

return new CreateCustomerResponse { CustomerId = customer.Id };
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Wax.Core.Domain.Customers;
using Wax.Core.Repositories;
using Wax.Messages.Commands.Customers;

namespace Wax.Core.Handlers.CommandHandlers.Customers;

public class DeleteCustomerCommandHandler: ICommandHandler<DeleteCustomerCommand>
{
private readonly ICustomerRepository _customerRepository;
private readonly IUnitOfWork _unitOfWork;

public DeleteCustomerCommandHandler(ICustomerRepository customerRepository)
public DeleteCustomerCommandHandler(IUnitOfWork unitOfWork)
{
_customerRepository = customerRepository;
_unitOfWork = unitOfWork;
}

public async Task Handle(IReceiveContext<DeleteCustomerCommand> context, CancellationToken cancellationToken)
{
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId, cancellationToken);
var customer = await _unitOfWork.Customers.GetByIdAsync(context.Message.CustomerId, cancellationToken);

await _customerRepository.DeleteAsync(customer, cancellationToken);
await _unitOfWork.Customers.DeleteAsync(customer, cancellationToken);
await _unitOfWork.SaveChangesAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
using AutoMapper;
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Wax.Core.Domain.Customers;
using Wax.Core.Domain.Customers.Exceptions;
using Wax.Core.Repositories;
using Wax.Messages.Commands.Customers;

namespace Wax.Core.Handlers.CommandHandlers.Customers;

public class UpdateCustomerCommandHandler : ICommandHandler<UpdateCustomerCommand>
{
private readonly IMapper _mapper;
private readonly ICustomerRepository _repository;
private readonly IUnitOfWork _unitOfWork;

public UpdateCustomerCommandHandler(IMapper mapper, ICustomerRepository repository)
public UpdateCustomerCommandHandler(IMapper mapper, IUnitOfWork unitOfWork)
{
_mapper = mapper;
_repository = repository;
_unitOfWork = unitOfWork;
}

public async Task Handle(IReceiveContext<UpdateCustomerCommand> context, CancellationToken cancellationToken)
{
var customer = await _repository.GetByIdAsync(context.Message.CustomerId, cancellationToken)
.ConfigureAwait(false);
var customer = await _unitOfWork.Customers.GetByIdAsync(context.Message.CustomerId, cancellationToken);

if (customer.Name != context.Message.Name)
{
if (!await _repository.CheckIsUniqueNameAsync(context.Message.Name, cancellationToken)
.ConfigureAwait(false))
var existing = await _unitOfWork.Customers.FindByNameAsync(context.Message.Name, cancellationToken);

if (existing != null)
{
throw new CustomerNameAlreadyExistsException();
}
}

_mapper.Map(context.Message, customer);

await _repository.UpdateAsync(customer, cancellationToken).ConfigureAwait(false);
await _unitOfWork.Customers.UpdateAsync(customer, cancellationToken).ConfigureAwait(false);
await _unitOfWork.SaveChangesAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Microsoft.EntityFrameworkCore;
using Wax.Core.Domain.Customers;
using Wax.Core.Repositories;
using Wax.Messages.Dtos.Customers;
using Wax.Messages.Requests.Customers;

Expand All @@ -12,23 +10,22 @@ namespace Wax.Core.Handlers.RequestHandlers.Customers;
public class GetCustomerRequestHandler : IRequestHandler<GetCustomerRequest, GetCustomerResponse>
{
private readonly IMapper _mapper;
private readonly ICustomerRepository _customerRepository;
private readonly IUnitOfWork _unitOfWork;

public GetCustomerRequestHandler(IMapper mapper, ICustomerRepository customerRepository)
public GetCustomerRequestHandler(IMapper mapper, IUnitOfWork unitOfWork)
{
_mapper = mapper;
_customerRepository = customerRepository;
_unitOfWork = unitOfWork;
}

public async Task<GetCustomerResponse> Handle(IReceiveContext<GetCustomerRequest> context,
CancellationToken cancellationToken)
{
var customer = await _unitOfWork.Customers.GetByIdAsync(context.Message.CustomerId, cancellationToken);

return new GetCustomerResponse
{
Customer = await _customerRepository.Query.AsNoTracking()
.Where(c => c.Id == context.Message.CustomerId)
.ProjectTo<CustomerShortInfo>(_mapper.ConfigurationProvider)
.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)
Customer = _mapper.Map<CustomerShortInfo>(customer)
};
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Wax.Core.Data;
using Wax.Core.Domain;
using Wax.Core.Exceptions;

namespace Wax.Core.Data.Repositories;
namespace Wax.Core.Repositories;

public class EfCoreRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
public class EfCoreBasicRepository<TEntity> : IBasicRepository<TEntity> where TEntity : class, IEntity
{
private readonly ApplicationDbContext _dbContext;

public EfCoreRepository(ApplicationDbContext dbContext)
public EfCoreBasicRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
Expand All @@ -16,7 +19,7 @@ public async Task<TEntity> GetByIdAsync<TKey>(TKey id, CancellationToken cancell
where TKey : notnull
{
var entity = await _dbContext.Set<TEntity>()
.FindAsync(new object?[] { id }, cancellationToken).ConfigureAwait(false);
.FindAsync(new object[] { id }, cancellationToken).ConfigureAwait(false);

if (entity == null)
{
Expand All @@ -29,37 +32,37 @@ public async Task<TEntity> GetByIdAsync<TKey>(TKey id, CancellationToken cancell
public async Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
{
await _dbContext.Set<TEntity>().AddAsync(entity, cancellationToken).ConfigureAwait(false);
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return entity;
}

public async Task<IEnumerable<TEntity>> InsertRangeAsync(IEnumerable<TEntity> entity,
CancellationToken cancellationToken = default)
{
await _dbContext.Set<TEntity>().AddRangeAsync(entity, cancellationToken).ConfigureAwait(false);
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return entity;
}

public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
public Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
{
_dbContext.Update(entity);
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
_dbContext.Entry(entity).State = EntityState.Modified;
return Task.CompletedTask;
}

public async Task UpdateRangeAsync(IEnumerable<TEntity> entities,
public Task UpdateRangeAsync(IEnumerable<TEntity> entities,
CancellationToken cancellationToken = default)
{
_dbContext.Set<TEntity>().UpdateRange(entities);

await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return Task.CompletedTask;
}

public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
public Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
{
_dbContext.Set<TEntity>().Remove(entity);
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
return Task.CompletedTask;
}

public IQueryable<TEntity> Query => _dbContext.Set<TEntity>();
public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate = null)
{
return predicate != null ? _dbContext.Set<TEntity>().Where(predicate) : _dbContext.Set<TEntity>();
}
}
18 changes: 18 additions & 0 deletions src/Wax.Core/Repositories/EfCoreCustomerRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Wax.Core.Data;
using Wax.Core.DependencyInjection;
using Wax.Core.Domain.Customers;

namespace Wax.Core.Repositories;

public class EfCoreCustomerRepository : EfCoreBasicRepository<Customer>, ICustomerRepository, IScopedDependency
{
public EfCoreCustomerRepository(ApplicationDbContext dbContext) : base(dbContext)
{
}

public Task<Customer> FindByNameAsync(string name, CancellationToken cancellationToken = default)
{
return Query(c => c.Name == name).FirstOrDefaultAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Linq.Expressions;
using Wax.Core.Domain;

namespace Wax.Core.Domain;
namespace Wax.Core.Repositories;

public interface IRepository<TEntity> where TEntity : class, IEntity
public interface IBasicRepository<TEntity> where TEntity : class, IEntity
{
Task<TEntity> GetByIdAsync<TKey>(TKey id, CancellationToken cancellationToken = default) where TKey : notnull;

Expand All @@ -17,5 +18,5 @@ Task<IEnumerable<TEntity>> InsertRangeAsync(IEnumerable<TEntity> entity,

Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);

IQueryable<TEntity> Query { get; }
IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate = null);
}
Loading

0 comments on commit 2f20109

Please sign in to comment.