Skip to content

Commit e4459de

Browse files
authored
Merge pull request #11 from sj-distributor/setting-and-repo
Add settings infrastructure and refactor repo pattern
2 parents a6b010e + 11894c8 commit e4459de

27 files changed

+317
-167
lines changed

src/Wax.Api/Startup.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,11 @@ public void ConfigureContainer(ContainerBuilder builder)
4242
var serviceProvider = _serviceCollection.BuildServiceProvider();
4343

4444
var currentUser = serviceProvider.GetRequiredService<ICurrentUser>();
45-
var connectionString = Configuration.GetConnectionString("Default");
4645

4746
// Register your own things directly with Autofac here. Don't
4847
// call builder.Populate(), that happens in AutofacServiceProviderFactory
4948
// for you.
50-
builder.RegisterModule(new ApplicationModule(Log.Logger, currentUser, connectionString,
49+
builder.RegisterModule(new ApplicationModule(Log.Logger, currentUser, Configuration,
5150
typeof(Startup).Assembly));
5251
}
5352

src/Wax.Core/ApplicationModule.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
using Mediator.Net;
55
using Mediator.Net.Autofac;
66
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.Extensions.Configuration;
78
using Serilog;
89
using Wax.Core.Data;
910
using Wax.Core.DependencyInjection;
1011
using Wax.Core.Middlewares.FluentMessageValidator;
1112
using Wax.Core.Middlewares.Logging;
13+
using Wax.Core.Middlewares.UnitOfWorks;
1214
using Wax.Core.Repositories;
1315
using Wax.Core.Services.Identity;
16+
using Wax.Core.Settings;
1417
using Module = Autofac.Module;
1518

1619
namespace Wax.Core
@@ -19,15 +22,15 @@ public class ApplicationModule : Module
1922
{
2023
private readonly ILogger _logger;
2124
private readonly ICurrentUser _currentUser;
22-
private readonly string _connectionString;
25+
private readonly IConfiguration _configuration;
2326
private readonly Assembly[] _assemblies;
2427

25-
public ApplicationModule(ILogger logger, ICurrentUser currentUser, string connectionString,
28+
public ApplicationModule(ILogger logger, ICurrentUser currentUser, IConfiguration configuration,
2629
params Assembly[] assemblies)
2730
{
2831
_logger = logger;
2932
_currentUser = currentUser;
30-
_connectionString = connectionString;
33+
_configuration = configuration;
3134

3235
_assemblies = (assemblies ?? Array.Empty<Assembly>())
3336
.Concat(new[] { typeof(ApplicationModule).Assembly })
@@ -44,6 +47,19 @@ protected override void Load(ContainerBuilder builder)
4447
RegisterMediator(builder);
4548
RegisterValidator(builder);
4649
}
50+
51+
private void RegisterSettings(ContainerBuilder builder)
52+
{
53+
builder.RegisterInstance(_configuration)
54+
.As<IConfiguration>()
55+
.SingleInstance();
56+
57+
var settingTypes = _assemblies.SelectMany(a => a.GetTypes())
58+
.Where(t => t.IsClass && typeof(IConfigurationSetting).IsAssignableFrom(t))
59+
.ToArray();
60+
61+
builder.RegisterTypes(settingTypes).AsSelf().SingleInstance();
62+
}
4763

4864
private void RegisterAutoMapper(ContainerBuilder builder)
4965
{
@@ -70,7 +86,9 @@ private void RegisterDatabase(ContainerBuilder builder)
7086
{
7187
builder.Register(c =>
7288
{
73-
if (!string.IsNullOrEmpty(_connectionString))
89+
var connectionString = _configuration.GetConnectionString("Default");
90+
91+
if (!string.IsNullOrEmpty(connectionString))
7492
{
7593
//Select your database provider
7694
}
@@ -83,10 +101,10 @@ private void RegisterDatabase(ContainerBuilder builder)
83101
}).AsSelf().As<DbContext>()
84102
.InstancePerLifetimeScope();
85103

86-
builder.RegisterGeneric(typeof(EfCoreBasicRepository<>))
104+
builder.RegisterGeneric(typeof(BasicRepository<>))
87105
.As(typeof(IBasicRepository<>))
88106
.InstancePerLifetimeScope();
89-
107+
90108
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
91109
}
92110

@@ -111,6 +129,7 @@ private void RegisterMediator(ContainerBuilder builder)
111129
{
112130
c.UseLogger();
113131
c.UseMessageValidator();
132+
c.UseUnitOfWork();
114133
});
115134

116135
builder.RegisterMediator(mediatorBuilder);

src/Wax.Core/Data/ApplicationDbContext.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : ba
99
{
1010
}
1111

12+
public bool HasEntitiesChanged { get; private set; }
13+
1214
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
1315
{
1416
optionsBuilder.UseInMemoryDatabase("__wax_database");
@@ -18,4 +20,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
1820
{
1921
modelBuilder.ApplyConfigurationsFromAssembly(typeof(CustomerEntityTypeConfiguration).Assembly);
2022
}
23+
24+
25+
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
26+
{
27+
var result = base.SaveChangesAsync(cancellationToken);
28+
29+
HasEntitiesChanged = false;
30+
31+
return result;
32+
}
33+
34+
public async Task ChangeEntitiesAsync(bool saveNow = false, CancellationToken cancellationToken = default)
35+
{
36+
HasEntitiesChanged = true;
37+
38+
if (saveNow)
39+
{
40+
await SaveChangesAsync(cancellationToken);
41+
}
42+
}
2143
}

src/Wax.Core/Data/IUnitOfWork.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Wax.Core.Data;
2+
3+
public interface IUnitOfWork
4+
{
5+
Task CommitAsync(CancellationToken cancellationToken = default);
6+
}
7+
8+
public class UnitOfWork : IUnitOfWork
9+
{
10+
private readonly ApplicationDbContext _context;
11+
12+
public UnitOfWork(ApplicationDbContext context)
13+
{
14+
_context = context;
15+
}
16+
17+
public Task CommitAsync(CancellationToken cancellationToken = default)
18+
{
19+
return _context.HasEntitiesChanged ? _context.SaveChangesAsync(cancellationToken) : Task.CompletedTask;
20+
}
21+
}

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using AutoMapper;
22
using Mediator.Net.Context;
33
using Mediator.Net.Contracts;
4+
using Wax.Core.Data;
45
using Wax.Core.Domain.Customers;
56
using Wax.Core.Domain.Customers.Exceptions;
67
using Wax.Core.Repositories;
@@ -11,18 +12,19 @@ namespace Wax.Core.Handlers.CommandHandlers.Customers
1112
public class CreateCustomerCommandHandler : ICommandHandler<CreateCustomerCommand, CreateCustomerResponse>
1213
{
1314
private readonly IMapper _mapper;
15+
private readonly ICustomerRepository _customerRepository;
1416
private readonly IUnitOfWork _unitOfWork;
1517

16-
public CreateCustomerCommandHandler(IMapper mapper, IUnitOfWork unitOfWork)
18+
public CreateCustomerCommandHandler(IMapper mapper,ICustomerRepository customerRepository)
1719
{
1820
_mapper = mapper;
19-
_unitOfWork = unitOfWork;
21+
_customerRepository = customerRepository;
2022
}
2123

2224
public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerCommand> context,
2325
CancellationToken cancellationToken)
2426
{
25-
var existing = await _unitOfWork.Customers.FindByNameAsync(context.Message.Name, cancellationToken);
27+
var existing = await _customerRepository.FindByNameAsync(context.Message.Name);
2628

2729
if (existing != null)
2830
{
@@ -31,8 +33,7 @@ public async Task<CreateCustomerResponse> Handle(IReceiveContext<CreateCustomerC
3133

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

34-
await _unitOfWork.Customers.InsertAsync(customer, cancellationToken).ConfigureAwait(false);
35-
await _unitOfWork.SaveChangesAsync(cancellationToken);
36+
await _customerRepository.InsertAsync(customer);
3637

3738
return new CreateCustomerResponse { CustomerId = customer.Id };
3839
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
using Mediator.Net.Context;
22
using Mediator.Net.Contracts;
3+
using Wax.Core.Data;
34
using Wax.Core.Repositories;
45
using Wax.Messages.Commands.Customers;
56

67
namespace Wax.Core.Handlers.CommandHandlers.Customers;
78

89
public class DeleteCustomerCommandHandler: ICommandHandler<DeleteCustomerCommand>
910
{
10-
private readonly IUnitOfWork _unitOfWork;
11+
private readonly ICustomerRepository _customerRepository;
1112

12-
public DeleteCustomerCommandHandler(IUnitOfWork unitOfWork)
13+
public DeleteCustomerCommandHandler(ICustomerRepository customerRepository)
1314
{
14-
_unitOfWork = unitOfWork;
15+
_customerRepository = customerRepository;
1516
}
1617

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

21-
await _unitOfWork.Customers.DeleteAsync(customer, cancellationToken);
22-
await _unitOfWork.SaveChangesAsync(cancellationToken);
22+
await _customerRepository.DeleteAsync(customer);
2323
}
2424
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ namespace Wax.Core.Handlers.CommandHandlers.Customers;
1010
public class UpdateCustomerCommandHandler : ICommandHandler<UpdateCustomerCommand>
1111
{
1212
private readonly IMapper _mapper;
13-
private readonly IUnitOfWork _unitOfWork;
13+
private readonly ICustomerRepository _customerRepository;
1414

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

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

2525
if (customer.Name != context.Message.Name)
2626
{
27-
var existing = await _unitOfWork.Customers.FindByNameAsync(context.Message.Name, cancellationToken);
27+
var existing = await _customerRepository.FindByNameAsync(context.Message.Name);
2828

2929
if (existing != null)
3030
{
@@ -34,7 +34,6 @@ public async Task Handle(IReceiveContext<UpdateCustomerCommand> context, Cancell
3434

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

37-
await _unitOfWork.Customers.UpdateAsync(customer, cancellationToken).ConfigureAwait(false);
38-
await _unitOfWork.SaveChangesAsync(cancellationToken);
37+
await _customerRepository.UpdateAsync(customer);
3938
}
4039
}

src/Wax.Core/Handlers/RequestHandlers/Customers/GetCustomerRequestHandler.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using AutoMapper;
22
using Mediator.Net.Context;
33
using Mediator.Net.Contracts;
4+
using Wax.Core.Data;
45
using Wax.Core.Repositories;
56
using Wax.Messages.Dtos.Customers;
67
using Wax.Messages.Requests.Customers;
@@ -10,18 +11,18 @@ namespace Wax.Core.Handlers.RequestHandlers.Customers;
1011
public class GetCustomerRequestHandler : IRequestHandler<GetCustomerRequest, GetCustomerResponse>
1112
{
1213
private readonly IMapper _mapper;
13-
private readonly IUnitOfWork _unitOfWork;
14+
private readonly ICustomerRepository _customerRepository;
1415

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

2122
public async Task<GetCustomerResponse> Handle(IReceiveContext<GetCustomerRequest> context,
2223
CancellationToken cancellationToken)
2324
{
24-
var customer = await _unitOfWork.Customers.GetByIdAsync(context.Message.CustomerId, cancellationToken);
25+
var customer = await _customerRepository.GetByIdAsync(context.Message.CustomerId);
2526

2627
return new GetCustomerResponse
2728
{
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Mediator.Net;
2+
using Mediator.Net.Context;
3+
using Mediator.Net.Contracts;
4+
using Mediator.Net.Pipeline;
5+
using Wax.Core.Data;
6+
7+
namespace Wax.Core.Middlewares.UnitOfWorks;
8+
9+
public static class UnitOfWorkMiddleware
10+
{
11+
public static void UseUnitOfWork<TContext>(this IPipeConfigurator<TContext> configurator)
12+
where TContext : IContext<IMessage>
13+
{
14+
if (configurator.DependencyScope == null)
15+
{
16+
throw new DependencyScopeNotConfiguredException("IDependencyScope is not configured.");
17+
}
18+
19+
var uow = configurator.DependencyScope.Resolve<IUnitOfWork>();
20+
21+
configurator.AddPipeSpecification(new UnitOfWorkSpecification<TContext>(uow));
22+
}
23+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Runtime.ExceptionServices;
2+
using Mediator.Net.Context;
3+
using Mediator.Net.Contracts;
4+
using Mediator.Net.Pipeline;
5+
using Wax.Core.Data;
6+
7+
namespace Wax.Core.Middlewares.UnitOfWorks;
8+
9+
public class UnitOfWorkSpecification<TContext> : IPipeSpecification<TContext>
10+
where TContext : IContext<IMessage>
11+
{
12+
private readonly IUnitOfWork _unitOfWork;
13+
14+
public UnitOfWorkSpecification(IUnitOfWork unitOfWork)
15+
{
16+
_unitOfWork = unitOfWork;
17+
}
18+
19+
public bool ShouldExecute(TContext context, CancellationToken cancellationToken)
20+
{
21+
return context.Message is ICommand or IEvent;
22+
}
23+
24+
public Task BeforeExecute(TContext context, CancellationToken cancellationToken)
25+
{
26+
return Task.CompletedTask;
27+
}
28+
29+
public Task Execute(TContext context, CancellationToken cancellationToken)
30+
{
31+
return Task.CompletedTask;
32+
}
33+
34+
public Task AfterExecute(TContext context, CancellationToken cancellationToken)
35+
{
36+
return ShouldExecute(context, cancellationToken)
37+
? _unitOfWork.CommitAsync(cancellationToken)
38+
: Task.CompletedTask;
39+
}
40+
41+
public Task OnException(Exception ex, TContext context)
42+
{
43+
ExceptionDispatchInfo.Capture(ex).Throw();
44+
return Task.CompletedTask;
45+
}
46+
}

0 commit comments

Comments
 (0)