Changed domains, configured links between VisaApplication and Applicant,created generic repository and repositories for domains

This commit is contained in:
2024-08-14 18:49:42 +03:00
parent e0f9a0f10f
commit e3d0d05355
35 changed files with 237 additions and 80 deletions

View File

@@ -1,4 +1,6 @@
namespace Domains.Common using Domains.LocationDomain;
namespace Domains.ApplicantDomain
{ {
/// Model of address /// Model of address
/// <remarks>Owned</remarks> /// <remarks>Owned</remarks>

View File

@@ -1,4 +1,5 @@
using Domains.Common; using Domains.LocationDomain;
using Domains.VisaApplicationDomain;
namespace Domains.ApplicantDomain namespace Domains.ApplicantDomain
{ {
@@ -49,5 +50,8 @@ namespace Domains.ApplicantDomain
/// Is <see cref="Applicant"/> a non-resident /// Is <see cref="Applicant"/> a non-resident
public bool IsNonResident { get; set; } public bool IsNonResident { get; set; }
/// List of <see cref="Applicant"/>'s applications
public List<VisaApplication> VisaApplications { get; set; } = null!;
} }
} }

View File

@@ -1,6 +1,4 @@
using Domains.Common; namespace Domains.ApplicantDomain
namespace Domains.ApplicantDomain
{ {
public class PlaceOfWork : IEntity public class PlaceOfWork : IEntity
{ {
@@ -10,7 +8,7 @@ namespace Domains.ApplicantDomain
/// Name of hirer /// Name of hirer
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
/// <see cref="Domains.Common.Address"/> of hirer /// <see cref="ApplicantDomain.Address"/> of hirer
public Address Address { get; set; } = null!; public Address Address { get; set; } = null!;
/// Phone number of hirer /// Phone number of hirer

View File

@@ -1,5 +1,8 @@
namespace Domains namespace Domains
{ {
/// Interface that every entity should inherit from /// Interface that every entity should inherit from
public interface IEntity { } public interface IEntity
{
public Guid Id { get; }
}
} }

View File

@@ -1,4 +1,4 @@
namespace Domains.Common namespace Domains.LocationDomain
{ {
/// Model of a city /// Model of a city
public class City : IEntity public class City : IEntity
@@ -9,7 +9,7 @@
/// Name of the city /// Name of the city
public string Name { get; set; } = null!; public string Name { get; set; } = null!;
/// <see cref="Domains.Common.Country"/> in which the city is located /// <see cref="LocationDomain.Country"/> in which the city is located
public Country Country { get; set; } = null!; public Country Country { get; set; } = null!;
} }
} }

View File

@@ -1,4 +1,4 @@
namespace Domains.Common namespace Domains.LocationDomain
{ {
/// Model of a country /// Model of a country
public class Country : IEntity public class Country : IEntity

View File

@@ -1,5 +1,6 @@
using Domains.ApplicantDomain; using System.ComponentModel.DataAnnotations.Schema;
using Domains.Common; using Domains.ApplicantDomain;
using Domains.LocationDomain;
namespace Domains.VisaApplicationDomain namespace Domains.VisaApplicationDomain
{ {
@@ -9,8 +10,11 @@ namespace Domains.VisaApplicationDomain
/// Unique identifier of <see cref="VisaApplication"/> /// Unique identifier of <see cref="VisaApplication"/>
public Guid Id { get; private set; } = Guid.NewGuid(); public Guid Id { get; private set; } = Guid.NewGuid();
/// Identifier of the <see cref="Applicant"/>
public Guid ApplicantId { get; set; }
/// Applicant of <see cref="VisaApplication"/> /// Applicant of <see cref="VisaApplication"/>
public Applicant Applicant { get; set; } = null!; public Applicant Applicant { get; set; }
/// <inheritdoc cref="Domains.VisaApplicationDomain.ReentryPermit"/> /// <inheritdoc cref="Domains.VisaApplicationDomain.ReentryPermit"/>
/// <remarks>always null if <see cref="Applicant"/> is not a non-resident</remarks> /// <remarks>always null if <see cref="Applicant"/> is not a non-resident</remarks>

View File

@@ -1,8 +1,8 @@
using Domains.Common; using Domains.ApplicantDomain;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Common namespace Infrastructure.Database.Applicants.Configuration
{ {
public class AddressConfiguration : IEntityTypeConfiguration<Address> public class AddressConfiguration : IEntityTypeConfiguration<Address>
{ {

View File

@@ -1,11 +1,12 @@
using Microsoft.EntityFrameworkCore; using Domains.ApplicantDomain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Applicant namespace Infrastructure.Database.Applicants.Configuration
{ {
public class ApplicantConfiguration : IEntityTypeConfiguration<Domains.ApplicantDomain.Applicant> public class ApplicantConfiguration : IEntityTypeConfiguration<Applicant>
{ {
public void Configure(EntityTypeBuilder<Domains.ApplicantDomain.Applicant> entity) public void Configure(EntityTypeBuilder<Applicant> entity)
{ {
entity.ToTable("Applicants"); entity.ToTable("Applicants");

View File

@@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Applicant namespace Infrastructure.Database.Applicants.Configuration
{ {
public class NameConfiguration : IEntityTypeConfiguration<Name> public class NameConfiguration : IEntityTypeConfiguration<Name>
{ {

View File

@@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Applicant namespace Infrastructure.Database.Applicants.Configuration
{ {
public class PassportConfiguration : IEntityTypeConfiguration<Passport> public class PassportConfiguration : IEntityTypeConfiguration<Passport>
{ {

View File

@@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Applicant namespace Infrastructure.Database.Applicants.Configuration
{ {
public class PlaceOfWorkConfiguration : IEntityTypeConfiguration<PlaceOfWork> public class PlaceOfWorkConfiguration : IEntityTypeConfiguration<PlaceOfWork>
{ {

View File

@@ -0,0 +1,18 @@
using Domains.ApplicantDomain;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Applicants.Repositories
{
public class ApplicantsRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
: GenericRepository<Applicant>(writer, unitOfWork), IApplicantsRepository
{
protected override IQueryable<Applicant> LoadDomain()
{
return reader.GetAll<Applicant>()
.Include(a => a.CountryOfBirth)
.Include(a => a.CityOfBirth)
.Include(a => a.PlaceOfWork);
}
}
}

View File

@@ -0,0 +1,7 @@
using Domains.ApplicantDomain;
using Infrastructure.Database.Generic;
namespace Infrastructure.Database.Applicants.Repositories
{
public interface IApplicantsRepository : IGenericRepository<Applicant> { }
}

View File

@@ -1,16 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.VisaApplication
{
public class VisaApplicationConfiguration : IEntityTypeConfiguration<Domains.VisaApplicationDomain.VisaApplication>
{
public void Configure(EntityTypeBuilder<Domains.VisaApplicationDomain.VisaApplication> entity)
{
entity.ToTable("VisaApplications");
entity.OwnsOne(p => p.ReentryPermit);
entity.OwnsOne(p => p.PermissionToDestCountry);
}
}
}

View File

@@ -1,41 +1,36 @@
using System.Reflection; using System.Reflection;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database namespace Infrastructure.Database
{ {
public class DbContext : Microsoft.EntityFrameworkCore.DbContext, IWriter, IReader, IUnitOfWork public class DbContext : Microsoft.EntityFrameworkCore.DbContext, IGenericWriter, IGenericReader, IUnitOfWork
{ {
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
} }
async Task IWriter.AddAsync<T>(T entity, CancellationToken cancellationToken) async Task IGenericWriter.AddAsync<T>(T entity, CancellationToken cancellationToken)
{ {
await AddAsync(entity, cancellationToken); await AddAsync(entity, cancellationToken);
} }
void IWriter.Update<T>(T entity) void IGenericWriter.Update<T>(T entity)
{ {
Update(entity); Update(entity);
} }
void IWriter.Remove<T>(T entity) void IGenericWriter.Remove<T>(T entity)
{ {
Remove(entity); Remove(entity);
} }
IQueryable<T> IReader.GetAll<T>() IQueryable<T> IGenericReader.GetAll<T>()
{ {
return Set<T>(); return Set<T>();
} }
async Task<T?> IReader.GetOneAsync<T>(Guid id, CancellationToken cancellationToken)
where T : class
{
return await Set<T>().FindAsync([id], cancellationToken: cancellationToken);
}
async Task IUnitOfWork.SaveAsync(CancellationToken cancellationToken) async Task IUnitOfWork.SaveAsync(CancellationToken cancellationToken)
{ {
await SaveChangesAsync(cancellationToken); await SaveChangesAsync(cancellationToken);

View File

@@ -0,0 +1,4 @@
namespace Infrastructure.Database.GeneralExceptions
{
public class EntityNotFoundException<T>(Guid id) : Exception($"Entity {typeof(T).Name} with id '{id}' not found");
}

View File

@@ -0,0 +1,38 @@
using Domains;
using Infrastructure.Database.GeneralExceptions;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Generic
{
public abstract class GenericRepository<T>(IGenericWriter writer, IUnitOfWork unitOfWork) : IGenericRepository<T>
where T : class, IEntity
{
public async Task<List<T>> GetAllAsync(CancellationToken cancellationToken)
=> await LoadDomain().ToListAsync(cancellationToken);
public async Task<T> GetOneAsync(Guid id, CancellationToken cancellationToken)
{
var result = await LoadDomain().SingleOrDefaultAsync(a => a.Id == id, cancellationToken);
return result ?? throw new EntityNotFoundException<T>(id);
}
public async Task AddAsync(T entity, CancellationToken cancellationToken)
=> await writer.AddAsync(entity, cancellationToken);
public async Task UpdateAsync(T entity, CancellationToken cancellationToken)
{
await GetOneAsync(entity.Id, cancellationToken);
writer.Update(entity);
}
public void Remove(T entity)
{
writer.Remove(entity);
}
public async Task SaveAsync(CancellationToken cancellationToken)
=> await unitOfWork.SaveAsync(cancellationToken);
protected abstract IQueryable<T> LoadDomain();
}
}

View File

@@ -0,0 +1,11 @@
using Domains;
namespace Infrastructure.Database.Generic
{
public interface IGenericReader
{
/// Get all entities of type <typeparamref name="T"/> stored in storage
/// <typeparam name="T">Entity type to seek in storage</typeparam>
IQueryable<T> GetAll<T>() where T : class, IEntity;
}
}

View File

@@ -0,0 +1,19 @@
using Domains;
namespace Infrastructure.Database.Generic
{
public interface IGenericRepository<T> where T : class, IEntity
{
Task<List<T>> GetAllAsync(CancellationToken cancellationToken);
Task<T> GetOneAsync(Guid id, CancellationToken cancellationToken);
Task AddAsync(T entity, CancellationToken cancellationToken);
Task UpdateAsync(T entity, CancellationToken cancellationToken);
void Remove(T entity);
Task SaveAsync(CancellationToken cancellationToken);
}
}

View File

@@ -1,10 +1,10 @@
using Domains; using Domains;
namespace Infrastructure.Database namespace Infrastructure.Database.Generic
{ {
/// Writes data to data storage /// Writes data to data storage
/// <remarks><see cref="IUnitOfWork"/> should be used to save changes</remarks> /// <remarks><see cref="IUnitOfWork"/> should be used to save changes</remarks>
public interface IWriter public interface IGenericWriter
{ {
/// Add <paramref name="entity"/> to data storage /// Add <paramref name="entity"/> to data storage
/// <param name="entity">Entity to add</param> /// <param name="entity">Entity to add</param>

View File

@@ -1,18 +0,0 @@
using Domains;
namespace Infrastructure.Database
{
public interface IReader
{
/// Get all entities of type <typeparamref name="T"/> stored in storage
/// <typeparam name="T">Entity type to seek in storage</typeparam>
IQueryable<T> GetAll<T>() where T : class, IEntity;
/// Get one entity with specific <paramref name="id"/> from storage
/// <param name="id">Identifier of entity</param>
/// <param name="cancellationToken">Cancellation Token</param>
/// <typeparam name="T">Type of entity</typeparam>
/// <returns>Entity or null if not found</returns>
Task<T?> GetOneAsync<T>(Guid id, CancellationToken cancellationToken) where T : class, IEntity;
}
}

View File

@@ -1,8 +1,8 @@
using Domains.Common; using Domains.LocationDomain;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Common namespace Infrastructure.Database.Locations.Configuration
{ {
public class CityConfiguration : IEntityTypeConfiguration<City> public class CityConfiguration : IEntityTypeConfiguration<City>
{ {

View File

@@ -1,8 +1,8 @@
using Domains.Common; using Domains.LocationDomain;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.Common namespace Infrastructure.Database.Locations.Configuration
{ {
public class CountryConfiguration : IEntityTypeConfiguration<Country> public class CountryConfiguration : IEntityTypeConfiguration<Country>
{ {

View File

@@ -0,0 +1,15 @@
using Domains.LocationDomain;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Locations.Repositories.Cities
{
public class CitiesRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
: GenericRepository<City>(writer, unitOfWork), ICitiesRepository
{
protected override IQueryable<City> LoadDomain()
{
return reader.GetAll<City>().Include(c => c.Country);
}
}
}

View File

@@ -0,0 +1,7 @@
using Domains.LocationDomain;
using Infrastructure.Database.Generic;
namespace Infrastructure.Database.Locations.Repositories.Cities
{
public interface ICitiesRepository : IGenericRepository<City> { }
}

View File

@@ -0,0 +1,15 @@
using Domains.LocationDomain;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.Locations.Repositories.Countries
{
public class CountriesRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
: GenericRepository<Country>(writer, unitOfWork), ICountriesRepository
{
protected override IQueryable<Country> LoadDomain()
{
return reader.GetAll<Country>().Include(c => c.Cities);
}
}
}

View File

@@ -0,0 +1,7 @@
using Domains.LocationDomain;
using Infrastructure.Database.Generic;
namespace Infrastructure.Database.Locations.Repositories.Countries
{
public interface ICountriesRepository : IGenericRepository<Country> { }
}

View File

@@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.VisaApplication namespace Infrastructure.Database.VisaApplications.Configuration
{ {
public class PastVisaConfiguration : IEntityTypeConfiguration<PastVisa> public class PastVisaConfiguration : IEntityTypeConfiguration<PastVisa>
{ {

View File

@@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.VisaApplication namespace Infrastructure.Database.VisaApplications.Configuration
{ {
public class PermissionToDestCountryConfiguration : IEntityTypeConfiguration<PermissionToDestCountry> public class PermissionToDestCountryConfiguration : IEntityTypeConfiguration<PermissionToDestCountry>
{ {

View File

@@ -2,7 +2,7 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.Configuration.VisaApplication namespace Infrastructure.Database.VisaApplications.Configuration
{ {
public class ReentryPermitConfiguration : IEntityTypeConfiguration<ReentryPermit> public class ReentryPermitConfiguration : IEntityTypeConfiguration<ReentryPermit>
{ {

View File

@@ -0,0 +1,22 @@
using Domains.VisaApplicationDomain;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Infrastructure.Database.VisaApplications.Configuration
{
public class VisaApplicationConfiguration : IEntityTypeConfiguration<VisaApplication>
{
public void Configure(EntityTypeBuilder<VisaApplication> entity)
{
entity.ToTable("VisaApplications");
entity.HasOne(va => va.Applicant)
.WithMany(a => a.VisaApplications)
.HasForeignKey(va => va.ApplicantId)
.IsRequired();
entity.OwnsOne(p => p.ReentryPermit);
entity.OwnsOne(p => p.PermissionToDestCountry);
}
}
}

View File

@@ -0,0 +1,7 @@
using Domains.VisaApplicationDomain;
using Infrastructure.Database.Generic;
namespace Infrastructure.Database.VisaApplications.Repositories
{
public interface IVisaApplicationsRepository : IGenericRepository<VisaApplication> { }
}

View File

@@ -0,0 +1,18 @@
using Domains.VisaApplicationDomain;
using Infrastructure.Database.Generic;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Database.VisaApplications.Repositories
{
public class VisaApplicationsRepository(IGenericReader reader, IGenericWriter writer, IUnitOfWork unitOfWork)
: GenericRepository<VisaApplication>(writer, unitOfWork), IVisaApplicationsRepository
{
protected override IQueryable<VisaApplication> LoadDomain()
{
return reader.GetAll<VisaApplication>()
.Include(a => a.DestinationCountry)
.Include(a => a.PastVisas)
.Include(a => a.PastVisits);
}
}
}

View File

@@ -15,8 +15,4 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0-preview.7.24405.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.0-preview.7.24405.3" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Database\Repositories\" />
</ItemGroup>
</Project> </Project>