Skip to content

Commit 2f20109

Browse files
authored
Merge pull request #9 from sj-distributor/repo2
Implementing the repository and uow patterns
2 parents 0750242 + c521599 commit 2f20109

23 files changed

+260
-128
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ WilTechs Architecture Solution Template for .NET 6
1010
Using dotnet cli template, install the template:
1111

1212
```
13-
dotnet new -i Wax.Template
13+
dotnet new install Wax.Template
1414
```
1515

1616
Run this command to create the solution:

src/Wax.Api/Filters/GlobalExceptionFilter.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ namespace Wax.Api.Filters;
77
public class GlobalExceptionFilter : IExceptionFilter
88
{
99
private readonly Serilog.ILogger _logger;
10+
private readonly IWebHostEnvironment _env;
1011

11-
public GlobalExceptionFilter(Serilog.ILogger logger)
12+
public GlobalExceptionFilter(Serilog.ILogger logger, IWebHostEnvironment env)
1213
{
1314
_logger = logger;
15+
_env = env;
1416
}
1517

1618
public void OnException(ExceptionContext context)
@@ -40,12 +42,12 @@ private void HandleBusinessException(ExceptionContext context)
4042

4143
var problemDetails = new ProblemDetails
4244
{
43-
Status = StatusCodes.Status403Forbidden,
44-
Title = "Business error",
45+
Status = StatusCodes.Status409Conflict,
46+
Title = "A business error occur.",
4547
Detail = context.Exception.Message,
46-
Instance = context.HttpContext.Request.Path
4748
};
4849

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

@@ -84,8 +86,8 @@ private void HandleInternalServerError(ExceptionContext context)
8486
var problemDetails = new ProblemDetails
8587
{
8688
Status = StatusCodes.Status500InternalServerError,
87-
Title = "Internal error",
88-
Detail = "An error occur.Try it again later."
89+
Title = "Internal error.",
90+
Detail = _env.IsDevelopment() ? context.Exception.Message : "An error occur. Try it again later."
8991
};
9092

9193
context.Result = new ObjectResult(problemDetails);

src/Wax.Core/ApplicationModule.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
using Microsoft.EntityFrameworkCore;
77
using Serilog;
88
using Wax.Core.Data;
9-
using Wax.Core.Data.Repositories;
109
using Wax.Core.DependencyInjection;
11-
using Wax.Core.Domain;
1210
using Wax.Core.Middlewares.FluentMessageValidator;
1311
using Wax.Core.Middlewares.Logging;
12+
using Wax.Core.Repositories;
1413
using Wax.Core.Services.Identity;
1514
using Module = Autofac.Module;
1615

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

87-
builder.RegisterGeneric(typeof(EfCoreRepository<>))
88-
.As(typeof(IRepository<>))
86+
builder.RegisterGeneric(typeof(EfCoreBasicRepository<>))
87+
.As(typeof(IBasicRepository<>))
8988
.InstancePerLifetimeScope();
89+
90+
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
9091
}
9192

9293
private void RegisterIdentity(ContainerBuilder builder)

src/Wax.Core/Data/Repositories/EfCoreCustomerRepository.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/Wax.Core/Domain/Customers/ICustomerRepository.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/Wax.Core/Handlers/CommandHandlers/Customers/CreateCustomerCommandHandler.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,36 @@
33
using Mediator.Net.Contracts;
44
using Wax.Core.Domain.Customers;
55
using Wax.Core.Domain.Customers.Exceptions;
6+
using Wax.Core.Repositories;
67
using Wax.Messages.Commands.Customers;
78

89
namespace Wax.Core.Handlers.CommandHandlers.Customers
910
{
1011
public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
1112
{
1213
private readonly IMapper _mapper;
13-
private readonly ICustomerRepository _repository;
14+
private readonly IUnitOfWork _unitOfWork;
1415

15-
public CreateCustomerCommandHandler(IMapper mapper, ICustomerRepository repository)
16+
public CreateCustomerCommandHandler(IMapper mapper, IUnitOfWork unitOfWork)
1617
{
1718
_mapper = mapper;
18-
_repository = repository;
19+
_unitOfWork = unitOfWork;
1920
}
2021

2122
public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
2223
CancellationToken cancellationToken)
2324
{
24-
if (!await _repository.CheckIsUniqueNameAsync(context.Message.Name, cancellationToken)
25-
.ConfigureAwait(false))
25+
var existing = await _unitOfWork.Customers.FindByNameAsync(context.Message.Name, cancellationToken);
26+
27+
if (existing != null)
2628
{
2729
throw new CustomerNameAlreadyExistsException();
2830
}
2931

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

32-
await _repository.InsertAsync(customer, cancellationToken).ConfigureAwait(false);
34+
await _unitOfWork.Customers.InsertAsync(customer, cancellationToken).ConfigureAwait(false);
35+
await _unitOfWork.SaveChangesAsync(cancellationToken);
3336

3437
return new CreateCustomerResponse { CustomerId = customer.Id };
3538
}
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
using Mediator.Net.Context;
22
using Mediator.Net.Contracts;
3-
using Wax.Core.Domain.Customers;
3+
using Wax.Core.Repositories;
44
using Wax.Messages.Commands.Customers;
55

66
namespace Wax.Core.Handlers.CommandHandlers.Customers;
77

88
public class DeleteCustomerCommandHandler: ICommandHandler<DeleteCustomerCommand>
99
{
10-
private readonly ICustomerRepository _customerRepository;
10+
private readonly IUnitOfWork _unitOfWork;
1111

12-
public DeleteCustomerCommandHandler(ICustomerRepository customerRepository)
12+
public DeleteCustomerCommandHandler(IUnitOfWork unitOfWork)
1313
{
14-
_customerRepository = customerRepository;
14+
_unitOfWork = unitOfWork;
1515
}
1616

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

21-
await _customerRepository.DeleteAsync(customer, cancellationToken);
21+
await _unitOfWork.Customers.DeleteAsync(customer, cancellationToken);
22+
await _unitOfWork.SaveChangesAsync(cancellationToken);
2223
}
2324
}
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,40 @@
11
using AutoMapper;
22
using Mediator.Net.Context;
33
using Mediator.Net.Contracts;
4-
using Wax.Core.Domain.Customers;
54
using Wax.Core.Domain.Customers.Exceptions;
5+
using Wax.Core.Repositories;
66
using Wax.Messages.Commands.Customers;
77

88
namespace Wax.Core.Handlers.CommandHandlers.Customers;
99

1010
public class UpdateCustomerCommandHandler : ICommandHandler<UpdateCustomerCommand>
1111
{
1212
private readonly IMapper _mapper;
13-
private readonly ICustomerRepository _repository;
13+
private readonly IUnitOfWork _unitOfWork;
1414

15-
public UpdateCustomerCommandHandler(IMapper mapper, ICustomerRepository repository)
15+
public UpdateCustomerCommandHandler(IMapper mapper, IUnitOfWork unitOfWork)
1616
{
1717
_mapper = mapper;
18-
_repository = repository;
18+
_unitOfWork = unitOfWork;
1919
}
2020

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

2625
if (customer.Name != context.Message.Name)
2726
{
28-
if (!await _repository.CheckIsUniqueNameAsync(context.Message.Name, cancellationToken)
29-
.ConfigureAwait(false))
27+
var existing = await _unitOfWork.Customers.FindByNameAsync(context.Message.Name, cancellationToken);
28+
29+
if (existing != null)
3030
{
3131
throw new CustomerNameAlreadyExistsException();
3232
}
3333
}
3434

3535
_mapper.Map(context.Message, customer);
3636

37-
await _repository.UpdateAsync(customer, cancellationToken).ConfigureAwait(false);
37+
await _unitOfWork.Customers.UpdateAsync(customer, cancellationToken).ConfigureAwait(false);
38+
await _unitOfWork.SaveChangesAsync(cancellationToken);
3839
}
3940
}
Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using AutoMapper;
2-
using AutoMapper.QueryableExtensions;
32
using Mediator.Net.Context;
43
using Mediator.Net.Contracts;
5-
using Microsoft.EntityFrameworkCore;
6-
using Wax.Core.Domain.Customers;
4+
using Wax.Core.Repositories;
75
using Wax.Messages.Dtos.Customers;
86
using Wax.Messages.Requests.Customers;
97

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

17-
public GetCustomerRequestHandler(IMapper mapper, ICustomerRepository customerRepository)
15+
public GetCustomerRequestHandler(IMapper mapper, IUnitOfWork unitOfWork)
1816
{
1917
_mapper = mapper;
20-
_customerRepository = customerRepository;
18+
_unitOfWork = unitOfWork;
2119
}
2220

2321
public async Task<GetCustomerResponse> Handle(IReceiveContext<GetCustomerRequest> context,
2422
CancellationToken cancellationToken)
2523
{
24+
var customer = await _unitOfWork.Customers.GetByIdAsync(context.Message.CustomerId, cancellationToken);
25+
2626
return new GetCustomerResponse
2727
{
28-
Customer = await _customerRepository.Query.AsNoTracking()
29-
.Where(c => c.Id == context.Message.CustomerId)
30-
.ProjectTo<CustomerShortInfo>(_mapper.ConfigurationProvider)
31-
.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)
28+
Customer = _mapper.Map<CustomerShortInfo>(customer)
3229
};
3330
}
3431
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
using System.Linq.Expressions;
2+
using Microsoft.EntityFrameworkCore;
3+
using Wax.Core.Data;
14
using Wax.Core.Domain;
25
using Wax.Core.Exceptions;
36

4-
namespace Wax.Core.Data.Repositories;
7+
namespace Wax.Core.Repositories;
58

6-
public class EfCoreRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
9+
public class EfCoreBasicRepository<TEntity> : IBasicRepository<TEntity> where TEntity : class, IEntity
710
{
811
private readonly ApplicationDbContext _dbContext;
912

10-
public EfCoreRepository(ApplicationDbContext dbContext)
13+
public EfCoreBasicRepository(ApplicationDbContext dbContext)
1114
{
1215
_dbContext = dbContext;
1316
}
@@ -16,7 +19,7 @@ public async Task<TEntity> GetByIdAsync<TKey>(TKey id, CancellationToken cancell
1619
where TKey : notnull
1720
{
1821
var entity = await _dbContext.Set<TEntity>()
19-
.FindAsync(new object?[] { id }, cancellationToken).ConfigureAwait(false);
22+
.FindAsync(new object[] { id }, cancellationToken).ConfigureAwait(false);
2023

2124
if (entity == null)
2225
{
@@ -29,37 +32,37 @@ public async Task<TEntity> GetByIdAsync<TKey>(TKey id, CancellationToken cancell
2932
public async Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
3033
{
3134
await _dbContext.Set<TEntity>().AddAsync(entity, cancellationToken).ConfigureAwait(false);
32-
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
3335
return entity;
3436
}
3537

3638
public async Task<IEnumerable<TEntity>> InsertRangeAsync(IEnumerable<TEntity> entity,
3739
CancellationToken cancellationToken = default)
3840
{
3941
await _dbContext.Set<TEntity>().AddRangeAsync(entity, cancellationToken).ConfigureAwait(false);
40-
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
4142
return entity;
4243
}
4344

44-
public async Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
45+
public Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
4546
{
46-
_dbContext.Update(entity);
47-
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
47+
_dbContext.Entry(entity).State = EntityState.Modified;
48+
return Task.CompletedTask;
4849
}
4950

50-
public async Task UpdateRangeAsync(IEnumerable<TEntity> entities,
51+
public Task UpdateRangeAsync(IEnumerable<TEntity> entities,
5152
CancellationToken cancellationToken = default)
5253
{
5354
_dbContext.Set<TEntity>().UpdateRange(entities);
54-
55-
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
55+
return Task.CompletedTask;
5656
}
5757

58-
public async Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
58+
public Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
5959
{
6060
_dbContext.Set<TEntity>().Remove(entity);
61-
await _dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
61+
return Task.CompletedTask;
6262
}
6363

64-
public IQueryable<TEntity> Query => _dbContext.Set<TEntity>();
64+
public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate = null)
65+
{
66+
return predicate != null ? _dbContext.Set<TEntity>().Where(predicate) : _dbContext.Set<TEntity>();
67+
}
6568
}

0 commit comments

Comments
 (0)