Skip to content

Commit 0226694

Browse files
Sullivan008peter.kozak
authored andcommitted
* Separated the application into 4 parts: Application.Client, Application.Core, Application.DataAccessLayer, Application.BusinessLogicLayer.
* Application.DataAccessLayer: * Includes the DataBase Contexts (Write and Read Contexts). * Includes every DataBase Entities. * Includes DesignTimeDbContextFactory for generating database tables at design-time. * Includes Extensions for DataBase Context (you can see more information about this in: ParkingAppDbContextExtension.InitDatabase method). * Includes generated Database Migrations. * Application.Core: * This project includes all elements that can be used by any point in the application. * This project does not include any business logic. * Includes the general global error handling constants and models for the client application. * Includes Command and Query interfaces for MediatR. * Includes a global export abstraction and a JSON Export Service that implements this abstraction. * Includes the types for environment. * Includes static datasets (for example: environment variables). * Application.BusinessLogicLayer: * This project includes the Business Logic. * The business logic can be divided into modules. * The module folders contain the following: Commands- Querys with Handlers, Dtos, Services with Interfaces and Request- Response Models for Client Application Commands. * Includes the Command and Query abstractions for MediatR. * Application.Client: * This is the User Interface Layer. * Includes IoC DI Registers, with separate configuration files (for example: DataBase configurations, MediatR configurations, Scoped and Singleton services , etc.). * Includes each extension for the IoC DI (for example: Hosts, etc.). * Includes configuration settings for NLog. * Includes the ASPNETCORE_ENVIRONMENT environment variable configuration. * Includes starting the database migration when starting the application. * Includes client-side global error handling. * The unhandled errors appear in an error window. * The text that appears depends on whether the application is running in a developer or other environment. * Includes the RelayCommandAsync class that implements the ICommand interface. * Includes the ViewModelBase class that implements the INotifyPropertyChange interface. * Includes the windows, views, view models and commands that used by the client application. * Includes the services that used by the client application. * The application implements the following business logic: * The application stores data from a parking garage system. * The system stores the arrival and departure times. * The system stores the parking passes. There are two types of parking passes: * License Plate based (1 car). * Card based (1 card - up to several cars). * In case of a key partner, it is possible to give a percentage discount. * Multiple payment options: Daily, Weekly, Monthly. * Based on the data stored in the database, the user can perform the following operations in the client application: * Based on the license plate or card number entered in the input field, the itemized parking list is displayed. * The user can export the list to JSON format, which will be saved in the root directory of the application.
1 parent 8747600 commit 0226694

File tree

76 files changed

+2832
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2832
-0
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net5.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="MediatR" Version="9.0.0" />
9+
</ItemGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\Application.Core\Application.Core.csproj" />
13+
<ProjectReference Include="..\Application.Infrastructure\Application.DataAccessLayer.csproj" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Application.BusinessLogicLayer.Interfaces
2+
{
3+
public interface IBusinessLogicLayerMarker
4+
{ }
5+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using Application.DataAccessLayer.Context;
4+
using MediatR;
5+
6+
namespace Application.BusinessLogicLayer.MediatR.Abstractions
7+
{
8+
public abstract class CommandBase<TCommand, TResult> : IRequestHandler<TCommand, TResult> where TCommand : IRequest<TResult>
9+
{
10+
protected readonly ParkingAppDbContext Context;
11+
12+
protected CommandBase(ParkingAppDbContext context)
13+
{
14+
Context = context;
15+
}
16+
17+
public abstract Task<TResult> Handle(TCommand request, CancellationToken cancellationToken);
18+
}
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using Application.DataAccessLayer.Context;
4+
using MediatR;
5+
6+
namespace Application.BusinessLogicLayer.MediatR.Abstractions
7+
{
8+
public abstract class QueryBase<TQuery, TResult> : IRequestHandler<TQuery, TResult> where TQuery : IRequest<TResult>
9+
{
10+
protected ParkingAppReadonlyDbContext Context;
11+
12+
protected QueryBase(ParkingAppReadonlyDbContext context)
13+
{
14+
Context = context;
15+
}
16+
17+
public abstract Task<TResult> Handle(TQuery request, CancellationToken cancellationToken);
18+
}
19+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Application.BusinessLogicLayer.MediatR.Abstractions;
6+
using Application.BusinessLogicLayer.Modules.Parking.Dtos;
7+
using Application.BusinessLogicLayer.Modules.Parking.RequestModels;
8+
using Application.Core.MediatR.Structs;
9+
using Application.Core.Services.Export.Interfaces;
10+
using Application.Core.Services.Export.Models;
11+
using Application.DataAccessLayer.Context;
12+
using MediatR;
13+
14+
namespace Application.BusinessLogicLayer.Modules.Parking.Commands
15+
{
16+
public class ExportParkingListCommand : IRequest<Result>
17+
{
18+
public IReadOnlyCollection<ExportParkingListItemDto> ExportList { get; }
19+
20+
public ExportParkingListCommand(ExportParkingListRequestModel model)
21+
{
22+
ExportList = model.ExportList.Select(x => new ExportParkingListItemDto
23+
{
24+
LicensePlateNumber = x.LicensePlateNumber,
25+
StartDate = x.StartDate,
26+
EndDate = x.EndDate,
27+
IntervalInMinutes = x.IntervalInMinutes
28+
}).ToList();
29+
}
30+
}
31+
32+
public class ExportParkingListCommandHandler : CommandBase<ExportParkingListCommand, Result>
33+
{
34+
private readonly IJsonExportService _jsonExportService;
35+
36+
public ExportParkingListCommandHandler(ParkingAppDbContext context, IJsonExportService jsonExportService) : base(context)
37+
{
38+
_jsonExportService = jsonExportService;
39+
}
40+
41+
public override async Task<Result> Handle(ExportParkingListCommand request, CancellationToken cancellationToken)
42+
{
43+
ExportDataModel<ExportParkingListItemDto> exportDataModel = new()
44+
{
45+
DataSet = request.ExportList,
46+
FileName = "export-result"
47+
};
48+
49+
await _jsonExportService.ExportData(exportDataModel);
50+
51+
return Result.Success();
52+
}
53+
}
54+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace Application.BusinessLogicLayer.Modules.Parking.Dtos
4+
{
5+
public class ExportParkingListItemDto
6+
{
7+
public string LicensePlateNumber { get; init; }
8+
9+
public DateTime StartDate { get; init; }
10+
11+
public DateTime? EndDate { get; init; }
12+
13+
public double? IntervalInMinutes { get; init; }
14+
}
15+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Application.BusinessLogicLayer.MediatR.Abstractions;
6+
using Application.BusinessLogicLayer.Modules.Parking.RequestModels;
7+
using Application.BusinessLogicLayer.Modules.Parking.ResponseModels;
8+
using Application.DataAccessLayer.Context;
9+
using MediatR;
10+
using Microsoft.EntityFrameworkCore;
11+
12+
namespace Application.BusinessLogicLayer.Modules.Parking.Queries
13+
{
14+
public class GetParkingListByCardNumberQuery : IRequest<ParkingListResponseModel>
15+
{
16+
public string CardNumber { get; }
17+
18+
public GetParkingListByCardNumberQuery(GetParkingListByCardNumberRequestModel model)
19+
{
20+
CardNumber = !string.IsNullOrWhiteSpace(model.CardNumber)
21+
? model.CardNumber.Trim()
22+
: throw new ArgumentNullException(nameof(model.CardNumber), @"The value cannot be null!");
23+
}
24+
}
25+
26+
public class GetParkingListByCardNumberQueryHandler : QueryBase<GetParkingListByCardNumberQuery, ParkingListResponseModel>
27+
{
28+
public GetParkingListByCardNumberQueryHandler(ParkingAppReadonlyDbContext context) : base(context)
29+
{ }
30+
31+
public override async Task<ParkingListResponseModel> Handle(GetParkingListByCardNumberQuery request, CancellationToken cancellationToken)
32+
{
33+
ParkingListResponseModel result = new()
34+
{
35+
ParkingList = await Context.Cars.Include(x => x.Card)
36+
.Include(x => x.LoggedParkingPeriods)
37+
.Where(x => x.Card != null)
38+
.Where(x => x.Card.Number.ToLower() == request.CardNumber.ToLower())
39+
.SelectMany(x => x.LoggedParkingPeriods.Select(y => new ParkingListItemResponseModel
40+
{
41+
LicensePlateNumber = x.LicensePlateNumber,
42+
StartDate = y.StartDate,
43+
EndDate = y.EndDate
44+
}))
45+
.ToListAsync(cancellationToken)
46+
};
47+
48+
return result;
49+
}
50+
}
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Application.BusinessLogicLayer.MediatR.Abstractions;
6+
using Application.BusinessLogicLayer.Modules.Parking.RequestModels;
7+
using Application.BusinessLogicLayer.Modules.Parking.ResponseModels;
8+
using Application.DataAccessLayer.Context;
9+
using MediatR;
10+
using Microsoft.EntityFrameworkCore;
11+
12+
namespace Application.BusinessLogicLayer.Modules.Parking.Queries
13+
{
14+
public class GetParkingListByLicensePlateNumberQuery : IRequest<ParkingListResponseModel>
15+
{
16+
public string LicensePlateNumber { get; }
17+
18+
public GetParkingListByLicensePlateNumberQuery(GetParkingListByLicensePlateNumberRequestModel model)
19+
{
20+
LicensePlateNumber = !string.IsNullOrWhiteSpace(model.LicensePlateNumber)
21+
? model.LicensePlateNumber.Trim()
22+
: throw new ArgumentNullException(nameof(model.LicensePlateNumber), @"The value cannot be null!");
23+
}
24+
}
25+
26+
public class GetParkingListByLicensePlateNumberQueryHandler : QueryBase<GetParkingListByLicensePlateNumberQuery, ParkingListResponseModel>
27+
{
28+
public GetParkingListByLicensePlateNumberQueryHandler(ParkingAppReadonlyDbContext context) : base(context)
29+
{ }
30+
31+
public override async Task<ParkingListResponseModel> Handle(GetParkingListByLicensePlateNumberQuery request, CancellationToken cancellationToken)
32+
{
33+
ParkingListResponseModel result = new()
34+
{
35+
ParkingList = await Context.Cars.Include(x => x.LoggedParkingPeriods)
36+
.Where(x => x.Card == null)
37+
.Where(x => x.LicensePlateNumber.ToLower() == request.LicensePlateNumber.ToLower())
38+
.SelectMany(x => x.LoggedParkingPeriods.Select(y => new ParkingListItemResponseModel
39+
{
40+
LicensePlateNumber = x.LicensePlateNumber,
41+
StartDate = y.StartDate,
42+
EndDate = y.EndDate
43+
}))
44+
.ToListAsync(cancellationToken)
45+
};
46+
47+
return result;
48+
}
49+
}
50+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace Application.BusinessLogicLayer.Modules.Parking.RequestModels
4+
{
5+
public class ExportParkingListItemRequestModel
6+
{
7+
public string LicensePlateNumber { get; init; }
8+
9+
public DateTime StartDate { get; init; }
10+
11+
public DateTime? EndDate { get; init; }
12+
13+
public double? IntervalInMinutes { get; init; }
14+
}
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Collections.Generic;
2+
3+
namespace Application.BusinessLogicLayer.Modules.Parking.RequestModels
4+
{
5+
public class ExportParkingListRequestModel
6+
{
7+
public IReadOnlyCollection<ExportParkingListItemRequestModel> ExportList { get; init; }
8+
}
9+
}

0 commit comments

Comments
 (0)