Skip to content

Commit

Permalink
#23 Repository pattern implementation (#25)
Browse files Browse the repository at this point in the history
* #23 Repository pattern implementation

* fix tests
  • Loading branch information
nkz-soft authored Nov 4, 2022
1 parent bd7db16 commit 9050bab
Show file tree
Hide file tree
Showing 22 changed files with 116 additions and 44 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ I would like this project to help you find simplified and effortless solutions.
- [ ] HealthCheck
- [ ] Add caching for EF Core
- [ ] MongoDB data provider
- [ ] Migrate to .NET 7

## Technologies used

Expand Down Expand Up @@ -69,3 +70,5 @@ I would like this project to help you find simplified and effortless solutions.
[FluentResults](https://github.com/altmann/FluentResults): FluentResults is a lightweight .NET library developed to solve a common problem. It returns an object indicating success or failure of an operation instead of throwing/using exceptions.

[Specification](https://github.com/ardalis/Specification): Base class with tests for adding specifications to a DDD model. Also includes a default generic Repository base class with support for EF6 and EF Core.

[Scrutor](https://github.com/khellang/Scrutor): Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace NKZSoft.Template.Application.Common.Repositories;

public interface IToDoItemRepository : IRepositoryBase<ToDoItem>
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace NKZSoft.Template.Application.Common.Repositories;

public interface IToDoListRepository : IRepositoryBase<ToDoList>
{

}
1 change: 1 addition & 0 deletions src/NKZSoft.Template.Application/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
global using MassTransit;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using NKZSoft.Template.Application.TodoItems.Specifications;
global using NKZSoft.Template.Common;

global using NKZSoft.Template.Domain.AggregatesModel.ToDoAggregates.Entities;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
namespace NKZSoft.Template.Application.TodoItems.Commands.Create;

using Common.Interfaces;
using Common.Repositories;

public sealed class CreateTodoItemCommandHandler : IRequestHandler<CreateToDoItemCommand, Result<Guid>>
{
private readonly IApplicationDbContext _context;
private readonly IToDoItemRepository _repository;

public CreateTodoItemCommandHandler(IApplicationDbContext context) => _context = context;
public CreateTodoItemCommandHandler(IToDoItemRepository repository) => _repository = repository.ThrowIfNull();

public async Task<Result<Guid>> Handle(CreateToDoItemCommand request, CancellationToken cancellationToken)
{
var entity = new ToDoItem(request.Title);

await _context.Set<ToDoItem>()
await _repository
.AddAsync(entity, cancellationToken)
.ConfigureAwait(false);

await _context.SaveChangesAsync(cancellationToken)
await _repository.SaveChangesAsync(cancellationToken)
.ConfigureAwait(false);

return Result.Ok(entity.Id);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
namespace NKZSoft.Template.Application.TodoItems.Commands.Delete;

using Common.Exceptions;
using Common.Interfaces;
using Common.Repositories;

public sealed class DeleteTodoItemCommandHandler : IRequestHandler<DeleteTodoItemCommand>
{
private readonly IApplicationDbContext _context;
private readonly IToDoItemRepository _repository;

public DeleteTodoItemCommandHandler(IApplicationDbContext context) => _context = context;
public DeleteTodoItemCommandHandler(IToDoItemRepository repository) => _repository = repository;

public async Task<Unit> Handle(DeleteTodoItemCommand request, CancellationToken cancellationToken)
{
var entity = await _context.Set<ToDoItem>()
.FindAsync(new object[] { request.Id }, cancellationToken)
var entity = await _repository
.SingleOrDefaultAsync(new ToDoItemByIdSpecification(request.Id), cancellationToken)
.ConfigureAwait(false);

if (entity == null)
{
throw new NotFoundException(nameof(ToDoItem), request.Id);
}

_context.Set<ToDoItem>().Remove(entity);
await _context.SaveChangesAsync(cancellationToken)
await _repository.DeleteAsync(entity, cancellationToken).ConfigureAwait(false);
await _repository.SaveChangesAsync(cancellationToken)
.ConfigureAwait(false);

return Unit.Value;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
namespace NKZSoft.Template.Application.TodoItems.Commands.Update;

using Common.Exceptions;
using Common.Interfaces;
using Common.Repositories;

public sealed class UpdateTodoItemCommandHandler : IRequestHandler<UpdateTodoItemCommand>
{
private readonly IApplicationDbContext _context;
private readonly IToDoItemRepository _repository;

public UpdateTodoItemCommandHandler(IApplicationDbContext context)
{
_context = context;
}
public UpdateTodoItemCommandHandler(IToDoItemRepository repository) => _repository = repository.ThrowIfNull();

public async Task<Unit> Handle(UpdateTodoItemCommand request, CancellationToken cancellationToken)
{
var entity = await _context.Set<ToDoItem>()
.FindAsync(new object[] { request.Id }, cancellationToken)
var entity = await _repository
.SingleOrDefaultAsync(new ToDoItemByIdSpecification(request.Id), cancellationToken)
.ConfigureAwait(false);

if (entity == null)
Expand All @@ -25,7 +22,7 @@ public async Task<Unit> Handle(UpdateTodoItemCommand request, CancellationToken

entity.Update(request.Title, request.Description);

await _context.SaveChangesAsync(cancellationToken)
await _repository.SaveChangesAsync(cancellationToken)
.ConfigureAwait(false);

return Unit.Value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
using Common.Exceptions;
using Common.Handlers;
using Common.Interfaces;
using Common.Repositories;

public sealed class GetTodoItemQueryHandler : HandlerQueryBase<GetTodoItemQuery, Result<ToDoItemDto>>
{
public GetTodoItemQueryHandler(IApplicationDbContext applicationDbContext,
private readonly IToDoItemRepository _repository;

public GetTodoItemQueryHandler(
IToDoItemRepository repository,
IApplicationDbContext applicationDbContext,
ICurrentUserService currentUserService,
IMapper mapper)
: base(applicationDbContext, mapper, currentUserService)
{
}
: base(applicationDbContext, mapper, currentUserService) =>
_repository = repository.ThrowIfNull();

public override async Task<Result<ToDoItemDto>> Handle(GetTodoItemQuery request, CancellationToken cancellationToken)
{
var entity = await ContextDb.Set<ToDoItem>()
.AsNoTracking()
.Where(e => e.Id == request.Id)
.SingleOrDefaultAsync(cancellationToken)
var entity = await _repository
.SingleOrDefaultAsync(new ToDoItemByIdSpecification(request.Id, true), cancellationToken)
.ConfigureAwait(false);

entity.ThrowIfNull(new NotFoundException());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
namespace NKZSoft.Template.Application.TodoItems.Queries.GetPage;

using NKZSoft.Template.Application.Models;
using Specifications;
using Common.Handlers;
using Common.Interfaces;
using Common.Paging;
using Common.Repositories;

public sealed class GetPageTodoQueryHandler :
PagingQueryHandler<GetPageTodoItemsQuery, Result<CollectionViewModel<ToDoItemDto>>, ToDoItemDto>
{
public GetPageTodoQueryHandler(IApplicationDbContext context,
ICurrentUserService currentUserService, IMapper mapper) : base(context, mapper, currentUserService)
{
}
private readonly IToDoItemRepository _repository;

public GetPageTodoQueryHandler(
IToDoItemRepository repository,
IApplicationDbContext context,
ICurrentUserService currentUserService, IMapper mapper) : base(context, mapper, currentUserService) =>
_repository = repository.ThrowIfNull();

public override async Task<Result<CollectionViewModel<ToDoItemDto>>> Handle(GetPageTodoItemsQuery request,
CancellationToken cancellationToken)
{
var specification = ToDoItemSpecification.Create(request.PageContext);

var entities = await ContextDb.Set<ToDoItem>()
.AsNoTracking()
.WithSpecification(specification)
.ToListAsync(cancellationToken)
var entities = await _repository
.ListAsync(specification, cancellationToken)
.ConfigureAwait(false);

var dtoItems = await entities
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace NKZSoft.Template.Application.TodoItems.Specifications;

public sealed class ToDoItemByIdSpecification : Specification<ToDoItem>, ISingleResultSpecification<ToDoItem>
{
public ToDoItemByIdSpecification(Guid id, bool noTracking = false)
{
Query.Where(i => i.Id == id);
if (noTracking)
{
Query.AsNoTracking();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static Specification<ToDoItem> Create(IPageContext<ToDoItemFilter> pageCo
specificationBuilder.Take(pageContext.PageSize);
}

specificationBuilder.AsNoTracking();
return specification;
}

Expand Down
2 changes: 0 additions & 2 deletions src/NKZSoft.Template.Common/GuardClausesExtension.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
namespace NKZSoft.Template.Common;

using System.Diagnostics.CodeAnalysis;

public static class GuardExtension
{
public static T ThrowIfNull<T>(this T param) where T : class? => Guard.Against.Null(param, nameof(param));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace NKZSoft.Template.Persistence.PostgreSQL.Database.Configurations;

public class ThingConfiguration : AuditableConfiguration<ToDoItem>, IEntityTypeConfiguration<ToDoItem>
public class ToDoItemConfiguration : AuditableConfiguration<ToDoItem>, IEntityTypeConfiguration<ToDoItem>
{
public override void Configure(EntityTypeBuilder<ToDoItem> builder)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace NKZSoft.Template.Persistence.PostgreSQL.Database.Configurations
{
public class ThingItemConfiguration : AuditableConfiguration<ToDoList>, IEntityTypeConfiguration<ToDoList>
public class ToDoListConfiguration : AuditableConfiguration<ToDoList>, IEntityTypeConfiguration<ToDoList>
{
public override void Configure(EntityTypeBuilder<ToDoList> builder)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ public static IServiceCollection AddPersistence(this IServiceCollection services
});
services.AddScoped<IDbInitializer, DbInitializer>();
services.AddScoped<IApplicationDbContext, ApplicationDbContext>();

services.Scan(scan => scan
.FromAssemblies(Assembly.GetExecutingAssembly())
.AddClasses(classes => classes.AssignableTo(typeof(RepositoryBase<>)))
.AsMatchingInterface()
.WithScopedLifetime());

services.AddMediatR(Assembly.GetExecutingAssembly());
return services;
}
Expand Down
2 changes: 2 additions & 0 deletions src/NKZSoft.Template.Persistence.PostgreSQL/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
global using System;
global using System.Diagnostics.CodeAnalysis;
global using System.Reflection;
global using Ardalis.Specification;
global using Ardalis.Specification.EntityFrameworkCore;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.EntityFrameworkCore.Metadata.Builders;
global using Microsoft.EntityFrameworkCore.Design;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
<PackageReference Include="Scrutor" Version="4.2.0" />
<PackageReference Include="Serilog" Version="2.12.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace NKZSoft.Template.Persistence.PostgreSQL.Repositories;

using Application.Common.Repositories;

public class ToDoItemRepository : RepositoryBase<ToDoItem>, IToDoItemRepository
{
public ToDoItemRepository(IApplicationDbContext dbContext) : base(dbContext.AppDbContext)
{
}

public ToDoItemRepository(IApplicationDbContext dbContext, ISpecificationEvaluator specificationEvaluator) : base(dbContext.AppDbContext, specificationEvaluator)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace NKZSoft.Template.Persistence.PostgreSQL.Repositories;

using Application.Common.Repositories;

public class ToDoListRepository : RepositoryBase<ToDoList>, IToDoListRepository
{
public ToDoListRepository(IApplicationDbContext dbContext) : base(dbContext.AppDbContext)
{
}

public ToDoListRepository(IApplicationDbContext dbContext, ISpecificationEvaluator specificationEvaluator)
: base(dbContext.AppDbContext, specificationEvaluator)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>1.2.5</Version>
<Version>1.2.6</Version>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand Down
4 changes: 3 additions & 1 deletion tests/NKZSoft.Template.Application.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
global using Xunit;
global using MediatR;
global using Moq;
global using FluentAssertions;
global using FluentAssertions;
global using NKZSoft.Template.Application.Common.Repositories;
global using NKZSoft.Template.Persistence.PostgreSQL.Repositories;
3 changes: 3 additions & 0 deletions tests/NKZSoft.Template.Application.Tests/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ public static async void ConfigureServices(IServiceCollection services)
services.AddApplication();
services.TryAddSingleton(AppMockFactory.CreateCurrentUserServiceMock());
services.TryAddSingleton(await ApplicationDbContextFactory.CreateAsync());

services.TryAddScoped<IToDoItemRepository, ToDoItemRepository>();
services.TryAddScoped<IToDoListRepository, ToDoListRepository>();
}
}

0 comments on commit 9050bab

Please sign in to comment.