WebApi + ClientApp, GraphQL, Reflection
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.CommandHandlers.Generic;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.CommandHandlers;
|
||||
|
||||
public class EventsCommandsHandler : GenericCommandHandler<Event, EventDTO, int>
|
||||
{
|
||||
public EventsCommandsHandler(EventsContext ctx, ILogger<EventsCommandsHandler> logger, IMapper mapper)
|
||||
: base(ctx, logger, mapper)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using MobilityOne.Common.Commands;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Events.WebAPI.Contract.Command;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using AutoMapper;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.CommandHandlers.Generic;
|
||||
|
||||
public class GenericCommandHandler<TDal, TDto, TPK> : IRequestHandler<AddCommand<TDto, TPK>, TPK>,
|
||||
IRequestHandler<UpdateCommand<TDto>>,
|
||||
IRequestHandler<DeleteCommand<TDto, TPK>>
|
||||
where TDal: class, IHasIdAsPK<TPK>
|
||||
where TDto: IHasIdAsPK<TPK>
|
||||
where TPK : IEquatable<TPK>
|
||||
{
|
||||
protected DbContext Ctx { get; }
|
||||
protected ILogger Logger { get; }
|
||||
protected IMapper Mapper { get; }
|
||||
|
||||
protected GenericCommandHandler(DbContext ctx, ILogger logger, IMapper mapper)
|
||||
{
|
||||
Ctx = ctx;
|
||||
Logger = logger;
|
||||
Mapper = mapper;
|
||||
}
|
||||
|
||||
public virtual async Task<TPK> Handle(AddCommand<TDto, TPK> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = Mapper.Map<TDto, TDal>(request.Dto);
|
||||
Ctx.Add(entity);
|
||||
await Ctx.SaveChangesAsync(cancellationToken);
|
||||
return entity.Id;
|
||||
}
|
||||
|
||||
public virtual async Task Handle(UpdateCommand<TDto> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await Ctx.Set<TDal>().FindAsync(request.Dto.Id);
|
||||
if (entity != null)
|
||||
{
|
||||
Mapper.Map(request.Dto, entity);
|
||||
await Ctx.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError($"UpdateCommand<{typeof(TDto).Name}> : Invalid id #{request.Dto.Id}");
|
||||
throw new ArgumentException($"Invalid id: {request.Dto.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task Handle(DeleteCommand<TDto, TPK> request, CancellationToken cancellationToken)
|
||||
{
|
||||
await Ctx.Set<TDal>().Where(d => d.Id.Equals(request.Id)).ExecuteDeleteAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.CommandHandlers.Generic;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.CommandHandlers;
|
||||
|
||||
public class PeopleCommandsHandler : GenericCommandHandler<Person, PersonDTO, int>
|
||||
{
|
||||
public PeopleCommandsHandler(EventsContext ctx, ILogger<PeopleCommandsHandler> logger, IMapper mapper)
|
||||
: base(ctx, logger, mapper)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.Command;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Contract.Messages;
|
||||
using Events.WebAPI.Handlers.EF.CommandHandlers.Generic;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using MassTransit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MobilityOne.Common.Commands;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.CommandHandlers;
|
||||
|
||||
public class RegistrationsCommandsHandler : GenericCommandHandler<Registration, RegistrationDTO, int>
|
||||
{
|
||||
private readonly IPublishEndpoint publishEndpoint;
|
||||
|
||||
public RegistrationsCommandsHandler(
|
||||
EventsContext ctx,
|
||||
ILogger<RegistrationsCommandsHandler> logger,
|
||||
IMapper mapper,
|
||||
IPublishEndpoint publishEndpoint)
|
||||
: base(ctx, logger, mapper)
|
||||
{
|
||||
this.publishEndpoint = publishEndpoint;
|
||||
}
|
||||
|
||||
public override async Task<int> Handle(AddCommand<RegistrationDTO, int> request, CancellationToken cancellationToken)
|
||||
{
|
||||
int id = await base.Handle(request, cancellationToken);
|
||||
|
||||
await publishEndpoint.Publish(new RegistrationCreated
|
||||
{
|
||||
RegistrationId = id,
|
||||
PersonId = request.Dto.PersonId,
|
||||
EventId = request.Dto.EventId,
|
||||
SportId = request.Dto.SportId
|
||||
}, cancellationToken);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public override async Task Handle(UpdateCommand<RegistrationDTO> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await Ctx.Set<Registration>().SingleOrDefaultAsync(r => r.Id == request.Dto.Id, cancellationToken);
|
||||
if (entity == null)
|
||||
{
|
||||
Logger.LogError("UpdateCommand<{DtoName}> : Invalid id #{Id}", typeof(RegistrationDTO).Name, request.Dto.Id);
|
||||
throw new ArgumentException($"Invalid id: {request.Dto.Id}");
|
||||
}
|
||||
|
||||
int previousPersonId = entity.PersonId;
|
||||
int previousEventId = entity.EventId;
|
||||
int previousSportId = entity.SportId;
|
||||
|
||||
await base.Handle(request, cancellationToken);
|
||||
|
||||
await publishEndpoint.Publish(new RegistrationUpdated
|
||||
{
|
||||
RegistrationId = request.Dto.Id,
|
||||
PersonId = request.Dto.PersonId,
|
||||
EventId = request.Dto.EventId,
|
||||
SportId = request.Dto.SportId,
|
||||
PreviousPersonId = previousPersonId,
|
||||
PreviousEventId = previousEventId,
|
||||
PreviousSportId = previousSportId
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
public override async Task Handle(DeleteCommand<RegistrationDTO, int> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entity = await Ctx.Set<Registration>()
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync(r => r.Id == request.Id, cancellationToken);
|
||||
|
||||
if (entity == null)
|
||||
{
|
||||
Logger.LogError("DeleteCommand<{DtoName}> : Invalid id #{Id}", typeof(RegistrationDTO).Name, request.Id);
|
||||
throw new ArgumentException($"Invalid id: {request.Id}");
|
||||
}
|
||||
|
||||
await base.Handle(request, cancellationToken);
|
||||
|
||||
await publishEndpoint.Publish(new RegistrationDeleted
|
||||
{
|
||||
RegistrationId = entity.Id,
|
||||
PersonId = entity.PersonId,
|
||||
EventId = entity.EventId,
|
||||
SportId = entity.SportId
|
||||
}, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.CommandHandlers.Generic;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.CommandHandlers
|
||||
{
|
||||
public class SportsCommandsHandler : GenericCommandHandler<Sport, SportDTO, int>
|
||||
{
|
||||
public SportsCommandsHandler(EventsContext ctx, ILogger<SportsCommandsHandler> logger, IMapper mapper)
|
||||
: base(ctx, logger, mapper)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
|
||||
public partial class EventsContext : DbContext
|
||||
{
|
||||
public EventsContext(DbContextOptions<EventsContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<Country> Countries { get; set; }
|
||||
|
||||
public virtual DbSet<Event> Events { get; set; }
|
||||
|
||||
public virtual DbSet<Person> People { get; set; }
|
||||
|
||||
public virtual DbSet<Registration> Registrations { get; set; }
|
||||
|
||||
public virtual DbSet<Sport> Sports { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Country>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Code).HasName("country_pkey");
|
||||
|
||||
entity.ToTable("country");
|
||||
|
||||
entity.HasIndex(e => e.Name, "country_name_key").IsUnique();
|
||||
|
||||
entity.Property(e => e.Code)
|
||||
.HasMaxLength(3)
|
||||
.HasColumnName("code");
|
||||
entity.Property(e => e.Alpha3)
|
||||
.HasMaxLength(3)
|
||||
.IsFixedLength()
|
||||
.HasColumnName("alpha3");
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("name");
|
||||
entity.Property(e => e.Translations)
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("translations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Event>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("event_pkey");
|
||||
|
||||
entity.ToTable("event");
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.EventDate).HasColumnName("event_date");
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(150)
|
||||
.HasColumnName("name");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Person>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("person_pkey");
|
||||
|
||||
entity.ToTable("person");
|
||||
|
||||
entity.HasIndex(e => new { e.DocumentNumber, e.CountryCode }, "person_document_number_country_code_key").IsUnique();
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.AddressCountry)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("address_country");
|
||||
entity.Property(e => e.AddressLine)
|
||||
.HasMaxLength(200)
|
||||
.HasColumnName("address_line");
|
||||
entity.Property(e => e.BirthDate).HasColumnName("birth_date");
|
||||
entity.Property(e => e.City)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("city");
|
||||
entity.Property(e => e.ContactPhone)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("contact_phone");
|
||||
entity.Property(e => e.CountryCode)
|
||||
.HasMaxLength(3)
|
||||
.HasColumnName("country_code");
|
||||
entity.Property(e => e.DocumentNumber)
|
||||
.HasMaxLength(50)
|
||||
.HasColumnName("document_number");
|
||||
entity.Property(e => e.Email)
|
||||
.HasMaxLength(255)
|
||||
.HasColumnName("email");
|
||||
entity.Property(e => e.FirstName)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("first_name");
|
||||
entity.Property(e => e.FirstNameTranscription)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("first_name_transcription");
|
||||
entity.Property(e => e.LastName)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("last_name");
|
||||
entity.Property(e => e.LastNameTranscription)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("last_name_transcription");
|
||||
entity.Property(e => e.PostalCode)
|
||||
.HasMaxLength(20)
|
||||
.HasColumnName("postal_code");
|
||||
|
||||
entity.HasOne(d => d.CountryCodeNavigation).WithMany(p => p.People)
|
||||
.HasForeignKey(d => d.CountryCode)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("person_country_code_fkey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Registration>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("registration_pkey");
|
||||
|
||||
entity.ToTable("registration");
|
||||
|
||||
entity.HasIndex(e => new { e.PersonId, e.SportId, e.EventId }, "registration_person_id_sport_id_event_id_key").IsUnique();
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.EventId).HasColumnName("event_id");
|
||||
entity.Property(e => e.PersonId).HasColumnName("person_id");
|
||||
entity.Property(e => e.RegisteredAt)
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP")
|
||||
.HasColumnName("registered_at");
|
||||
entity.Property(e => e.SportId).HasColumnName("sport_id");
|
||||
|
||||
entity.HasOne(d => d.Event).WithMany(p => p.Registrations)
|
||||
.HasForeignKey(d => d.EventId)
|
||||
.HasConstraintName("registration_event_id_fkey");
|
||||
|
||||
entity.HasOne(d => d.Person).WithMany(p => p.Registrations)
|
||||
.HasForeignKey(d => d.PersonId)
|
||||
.HasConstraintName("registration_person_id_fkey");
|
||||
|
||||
entity.HasOne(d => d.Sport).WithMany(p => p.Registrations)
|
||||
.HasForeignKey(d => d.SportId)
|
||||
.HasConstraintName("registration_sport_id_fkey");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Sport>(entity =>
|
||||
{
|
||||
entity.HasKey(e => e.Id).HasName("sport_pkey");
|
||||
|
||||
entity.ToTable("sport");
|
||||
|
||||
entity.HasIndex(e => e.Name, "sport_name_key").IsUnique();
|
||||
|
||||
entity.Property(e => e.Id).HasColumnName("id");
|
||||
entity.Property(e => e.Name)
|
||||
.HasMaxLength(100)
|
||||
.HasColumnName("name");
|
||||
});
|
||||
|
||||
OnModelCreatingPartial(modelBuilder);
|
||||
}
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Events.WebAPI.Contract\Events.WebAPI.Contract.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||
<PackageReference Include="MassTransit" Version="8.5.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,39 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Mappings;
|
||||
|
||||
public class EFMappingProfile : Profile
|
||||
{
|
||||
public EFMappingProfile()
|
||||
{
|
||||
CreateMap<Event, EventDTO>()
|
||||
.ForMember(o => o.RegistrationsCount, opt => opt.MapFrom(src => src.Registrations.Count));
|
||||
CreateMap<EventDTO, Event>()
|
||||
.ForMember(o => o.Id, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Person, PersonDTO>()
|
||||
.ForMember(o => o.CountryName, opt => opt.MapFrom(src => src.CountryCodeNavigation.Name))
|
||||
.ForMember(o => o.FullNameTranscription, opt => opt.MapFrom(src => src.FirstNameTranscription + " " + src.LastNameTranscription))
|
||||
.ForMember(o => o.RegistrationsCount, opt => opt.MapFrom(src => src.Registrations.Count));
|
||||
CreateMap<PersonDTO, Person>()
|
||||
.ForMember(o => o.Id, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Registration, RegistrationDTO>()
|
||||
.ForMember(o => o.PersonName, opt => opt.MapFrom(src => src.Person.FirstName + " " + src.Person.LastName))
|
||||
.ForMember(o => o.PersonTranscription, opt => opt.MapFrom(src => src.Person.FirstNameTranscription + " " + src.Person.LastNameTranscription))
|
||||
.ForMember(o => o.PersonFirstNameTranscription, opt => opt.MapFrom(src => src.Person.FirstNameTranscription))
|
||||
.ForMember(o => o.PersonLastNameTranscription, opt => opt.MapFrom(src => src.Person.LastNameTranscription))
|
||||
.ForMember(o => o.CountryCode, opt => opt.MapFrom(src => src.Person.CountryCode))
|
||||
.ForMember(o => o.CountryName, opt => opt.MapFrom(src => src.Person.CountryCodeNavigation.Name))
|
||||
.ForMember(o => o.SportName, opt => opt.MapFrom(src => src.Sport.Name));
|
||||
CreateMap<RegistrationDTO, Registration>()
|
||||
.ForMember(o => o.Id, opt => opt.Ignore())
|
||||
.ForMember(o => o.RegisteredAt, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Sport, SportDTO>();
|
||||
CreateMap<SportDTO, Sport>()
|
||||
.ForMember(o => o.Id, opt => opt.Ignore());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Event : IHasIdAsPK<int>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Person : IHasIdAsPK<int>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Registration : IHasIdAsPK<int>
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Sport : IHasIdAsPK<int>
|
||||
{
|
||||
}
|
||||
19
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Country.cs
Normal file
19
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Country.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Country
|
||||
{
|
||||
public string Code { get; set; } = null!;
|
||||
|
||||
public string Alpha3 { get; set; } = null!;
|
||||
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
public string? Translations { get; set; }
|
||||
|
||||
public virtual ICollection<Person> People { get; set; } = new List<Person>();
|
||||
}
|
||||
17
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Event.cs
Normal file
17
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Event.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Event
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
public DateOnly EventDate { get; set; }
|
||||
|
||||
public virtual ICollection<Registration> Registrations { get; set; } = new List<Registration>();
|
||||
}
|
||||
41
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Person.cs
Normal file
41
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Person.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Person
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string? FirstName { get; set; }
|
||||
|
||||
public string? LastName { get; set; }
|
||||
|
||||
public string FirstNameTranscription { get; set; } = null!;
|
||||
|
||||
public string LastNameTranscription { get; set; } = null!;
|
||||
|
||||
public string? AddressLine { get; set; }
|
||||
|
||||
public string? PostalCode { get; set; }
|
||||
|
||||
public string? City { get; set; }
|
||||
|
||||
public string? AddressCountry { get; set; }
|
||||
|
||||
public string? Email { get; set; }
|
||||
|
||||
public string? ContactPhone { get; set; }
|
||||
|
||||
public DateOnly BirthDate { get; set; }
|
||||
|
||||
public string DocumentNumber { get; set; } = null!;
|
||||
|
||||
public string CountryCode { get; set; } = null!;
|
||||
|
||||
public virtual Country CountryCodeNavigation { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<Registration> Registrations { get; set; } = new List<Registration>();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Registration
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int PersonId { get; set; }
|
||||
|
||||
public int SportId { get; set; }
|
||||
|
||||
public int EventId { get; set; }
|
||||
|
||||
public DateTime RegisteredAt { get; set; }
|
||||
|
||||
public virtual Event Event { get; set; } = null!;
|
||||
|
||||
public virtual Person Person { get; set; } = null!;
|
||||
|
||||
public virtual Sport Sport { get; set; } = null!;
|
||||
}
|
||||
15
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Sport.cs
Normal file
15
Events-WebApi/Events.WebAPI.Handlers.EF/Models/Sport.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.Models;
|
||||
|
||||
public partial class Sport
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
public virtual ICollection<Registration> Registrations { get; set; } = new List<Registration>();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Contract.LookupQueries;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers;
|
||||
|
||||
public class CountriesLookupQueryHandler : IRequestHandler<LookupCountryQuery, List<IdName<string>>>
|
||||
{
|
||||
private readonly EventsContext ctx;
|
||||
|
||||
public CountriesLookupQueryHandler(EventsContext ctx)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public async Task<List<IdName<string>>> Handle(LookupCountryQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = ctx.Countries.AsNoTracking();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Text))
|
||||
{
|
||||
string text = request.Text.Trim();
|
||||
query = query.Where(c => Microsoft.EntityFrameworkCore.EF.Functions.ILike(c.Name, $"%{text}%"));
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderBy(c => c.Name)
|
||||
.Select(c => new IdName<string>
|
||||
{
|
||||
Id = c.Code,
|
||||
Name = c.Name
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Events.WebAPI.Handlers.EF.QueryHandlers.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sieve.Services;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers;
|
||||
|
||||
public class EventsQueryHandler : GenericQueryHandler<EventDTO, Event, int>
|
||||
{
|
||||
public EventsQueryHandler(EventsContext ctx, ILogger<EventsQueryHandler> logger, IMapper mapper, ISieveProcessor sieveProcessor)
|
||||
: base(ctx, logger, mapper, sieveProcessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Contract.Queries.Generic;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sieve.Models;
|
||||
using Sieve.Services;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers.Generic
|
||||
{
|
||||
public abstract class GenericQueryHandler<TDto, TDal, TPK> :
|
||||
IRequestHandler<GetSingleItemQuery<TDto, TPK>, TDto?>,
|
||||
IRequestHandler<GetCountQuery<TDto>, int>,
|
||||
IRequestHandler<DoesItemExistsQuery<TDto, TPK>, bool>,
|
||||
IRequestHandler<GetItemsQuery<TDto>, List<TDto>>
|
||||
where TDal : class, IHasIdAsPK<TPK>
|
||||
where TPK : IEquatable<TPK>
|
||||
{
|
||||
private readonly DbContext ctx;
|
||||
protected readonly ILogger logger;
|
||||
private readonly IMapper mapper;
|
||||
private readonly ISieveProcessor sieveProcessor;
|
||||
|
||||
public GenericQueryHandler(DbContext ctx, ILogger logger, IMapper mapper, ISieveProcessor sieveProcessor)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
this.logger = logger;
|
||||
this.mapper = mapper;
|
||||
this.sieveProcessor = sieveProcessor;
|
||||
}
|
||||
|
||||
public virtual async Task<int> Handle(GetCountQuery<TDto> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = ctx.Set<TDal>().AsNoTracking();
|
||||
IQueryable<TDto> projectedQuery = mapper.ProjectTo<TDto>(query);
|
||||
SieveModel sieveModel = new SieveModel()
|
||||
{
|
||||
Filters = request.Filters,
|
||||
};
|
||||
var filteredQuery = sieveProcessor.Apply(sieveModel, projectedQuery, applyFiltering: true, applySorting: false, applyPagination: false);
|
||||
|
||||
int count = await filteredQuery.CountAsync(cancellationToken);
|
||||
return count;
|
||||
}
|
||||
|
||||
public virtual async Task<TDto?> Handle(GetSingleItemQuery<TDto, TPK> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = ctx.Set<TDal>()
|
||||
.AsNoTracking()
|
||||
.Where(t => t.Id.Equals(request.Id));
|
||||
|
||||
IQueryable<TDto> projectedQuery = mapper.ProjectTo<TDto>(query);
|
||||
var item = await projectedQuery.FirstOrDefaultAsync(cancellationToken);
|
||||
return item;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> Handle(DoesItemExistsQuery<TDto, TPK> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = ctx.Set<TDal>()
|
||||
.AsNoTracking()
|
||||
.Where(t => t.Id.Equals(request.Id));
|
||||
|
||||
bool exists = await query.AnyAsync(cancellationToken);
|
||||
return exists;
|
||||
}
|
||||
|
||||
public virtual async Task<List<TDto>> Handle(GetItemsQuery<TDto> request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = ctx.Set<TDal>().AsNoTracking();
|
||||
IQueryable<TDto> projectedQuery = mapper.ProjectTo<TDto>(query);
|
||||
|
||||
SieveModel sieveModel = new SieveModel()
|
||||
{
|
||||
Filters = request.Filters,
|
||||
Sorts = BuildSortExpression(request),
|
||||
PageSize = request.PageSize,
|
||||
Page = request.Page
|
||||
};
|
||||
var filteredQuery = sieveProcessor.Apply(sieveModel, projectedQuery, applyFiltering: true, applySorting: true, applyPagination: true);
|
||||
|
||||
var data = await filteredQuery.ToListAsync(cancellationToken);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static string? BuildSortExpression(GetItemsQuery<TDto> request)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(request.Sort))
|
||||
{
|
||||
return request.Ascending ? request.Sort : "-" + request.Sort;
|
||||
}
|
||||
|
||||
bool paginationRequested = request.Page.HasValue || request.PageSize.HasValue;
|
||||
return paginationRequested ? nameof(IHasIdAsPK<int>.Id) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Contract.LookupQueries;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers;
|
||||
|
||||
public class PeopleLookupQueryHandler : IRequestHandler<LookupPeopleQuery, List<IdName<int>>>
|
||||
{
|
||||
private readonly EventsContext ctx;
|
||||
|
||||
public PeopleLookupQueryHandler(EventsContext ctx)
|
||||
{
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public async Task<List<IdName<int>>> Handle(LookupPeopleQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = ctx.People.AsNoTracking();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Text))
|
||||
{
|
||||
string text = request.Text.Trim();
|
||||
query = query.Where(p =>
|
||||
global::Microsoft.EntityFrameworkCore.EF.Functions.ILike(
|
||||
p.FirstNameTranscription + " " + p.LastNameTranscription,
|
||||
$"%{text}%"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.CountryCode))
|
||||
{
|
||||
string countryCode = request.CountryCode.Trim();
|
||||
query = query.Where(p => p.CountryCode == countryCode);
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderBy(p => p.FirstNameTranscription)
|
||||
.ThenBy(p => p.LastNameTranscription)
|
||||
.Select(p => new IdName<int>
|
||||
{
|
||||
Id = p.Id,
|
||||
Name = p.FirstName + " " + p.LastName,
|
||||
Description = p.FirstNameTranscription + " " + p.LastNameTranscription
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Events.WebAPI.Handlers.EF.QueryHandlers.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sieve.Services;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers;
|
||||
|
||||
public class PeopleQueryHandler : GenericQueryHandler<PersonDTO, Person, int>
|
||||
{
|
||||
public PeopleQueryHandler(EventsContext ctx, ILogger<PeopleQueryHandler> logger, IMapper mapper, ISieveProcessor sieveProcessor)
|
||||
: base(ctx, logger, mapper, sieveProcessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Events.WebAPI.Handlers.EF.QueryHandlers.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sieve.Services;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers;
|
||||
|
||||
public class RegistrationsQueryHandler : GenericQueryHandler<RegistrationDTO, Registration, int>
|
||||
{
|
||||
public RegistrationsQueryHandler(EventsContext ctx, ILogger<RegistrationsQueryHandler> logger, IMapper mapper, ISieveProcessor sieveProcessor)
|
||||
: base(ctx, logger, mapper, sieveProcessor)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using AutoMapper;
|
||||
using Events.WebAPI.Contract.DTOs;
|
||||
using Events.WebAPI.Handlers.EF.Data.Postgres;
|
||||
using Events.WebAPI.Handlers.EF.Models;
|
||||
using Events.WebAPI.Handlers.EF.QueryHandlers.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Sieve.Services;
|
||||
|
||||
namespace Events.WebAPI.Handlers.EF.QueryHandlers;
|
||||
|
||||
public class SportsQueryHandler : GenericQueryHandler<SportDTO, Sport, int>
|
||||
{
|
||||
public SportsQueryHandler(EventsContext ctx, ILogger<SportsQueryHandler> logger, IMapper mapper, ISieveProcessor sieveProcessor)
|
||||
: base(ctx, logger, mapper, sieveProcessor)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"CodeGenerationMode": 6,
|
||||
"ContextClassName": "EventsContext",
|
||||
"ContextNamespace": null,
|
||||
"FilterSchemas": false,
|
||||
"IncludeConnectionString": false,
|
||||
"IrregularWords": null,
|
||||
"MinimumProductVersion": "2.6.1465",
|
||||
"ModelNamespace": null,
|
||||
"OutputContextPath": "Data\/Postgres",
|
||||
"OutputPath": "Models",
|
||||
"PluralRules": null,
|
||||
"PreserveCasingWithRegex": true,
|
||||
"ProjectRootNamespace": "Events.WebAPI.Handlers.EF",
|
||||
"Schemas": null,
|
||||
"SelectedHandlebarsLanguage": 2,
|
||||
"SelectedToBeGenerated": 0,
|
||||
"SingularRules": null,
|
||||
"T4TemplatePath": null,
|
||||
"Tables": [
|
||||
{
|
||||
"Name": "public.country",
|
||||
"ObjectType": 0
|
||||
},
|
||||
{
|
||||
"Name": "public.event",
|
||||
"ObjectType": 0
|
||||
},
|
||||
{
|
||||
"Name": "public.person",
|
||||
"ObjectType": 0
|
||||
},
|
||||
{
|
||||
"Name": "public.registration",
|
||||
"ObjectType": 0
|
||||
},
|
||||
{
|
||||
"Name": "public.sport",
|
||||
"ObjectType": 0
|
||||
}
|
||||
],
|
||||
"UiHint": null,
|
||||
"UncountableWords": null,
|
||||
"UseAsyncStoredProcedureCalls": true,
|
||||
"UseBoolPropertiesWithoutDefaultSql": false,
|
||||
"UseDatabaseNames": false,
|
||||
"UseDatabaseNamesForRoutines": true,
|
||||
"UseDateOnlyTimeOnly": true,
|
||||
"UseDbContextSplitting": false,
|
||||
"UseDecimalDataAnnotationForSprocResult": true,
|
||||
"UseFluentApiOnly": true,
|
||||
"UseHandleBars": false,
|
||||
"UseHierarchyId": false,
|
||||
"UseInflector": true,
|
||||
"UseInternalAccessModifiersForSprocsAndFunctions": false,
|
||||
"UseLegacyPluralizer": false,
|
||||
"UseManyToManyEntity": false,
|
||||
"UseNoDefaultConstructor": true,
|
||||
"UseNoNavigations": false,
|
||||
"UseNoObjectFilter": false,
|
||||
"UseNodaTime": false,
|
||||
"UseNullableReferences": true,
|
||||
"UsePrefixNavigationNaming": false,
|
||||
"UseSchemaFolders": false,
|
||||
"UseSchemaNamespaces": false,
|
||||
"UseSpatial": false,
|
||||
"UseT4": false,
|
||||
"UseT4Split": false,
|
||||
"UseTypedTvpParameters": true
|
||||
}
|
||||
Reference in New Issue
Block a user