# Preparation: The unedited conversation with Chat GPT, concerning almost all of the aspects of this session: (optional): read this to get a better understanding of the topic: https://chatgpt.com/share/67f18460-1c1c-8010-bc57-9f3b683ec87a # Branching - [ ]  Create the feature/transportation-search branch based on develop # DTO In order to develop transportation search flow, three DTOs need to be created in the application layer. - [ ]  Create DTOs related to transportation search flow 📂 Suggested Folder: Application/DTOs/City` ```cs public class CityDto { public int Id { get; init; } public required string Title { get; init; } } ``` 📂 Suggested Folder: Application/DTOs/Transportation ```cs public class TransportationSearchRequestDto { public short? VehicleTypeId { get; init; } public int? FromCityId { get; init; } public int? ToCityId { get; init; } public DateTime? StartDate { get; init; } public DateTime? EndDate { get; init; } } ``` ```cs public class TransportationSearchResultDto { public long Id { get; init; } public required string CompanyTitle { get; init; } public required string FromLocationTitle { get; init; } public required string ToLocationTitle { get; init; } public required string FromCityTitle { get; init; } public required string ToCityTitle { get; init; } public DateTime StartDateTime { get; init; } public DateTime? EndDateTime { get; init; } public decimal Price { get; init; } } ``` # Repository There are a few things to be add to some repositories for transportation search flow. - [ ] Create DTOs related to transportation search flow 📂 Suggested Folder: Domain/Framework/Interfaces/Repositories/ TransportationRepositories ```cs public interface ITransportationRepository : IRepository { Task> SearchTransportationsAsync( short? vehicleTypeId, int? fromCityId, int? toCityId, DateTime? startDate, DateTime? endDate); } ``` 📂 Suggested Folder: Infrastructure/Services/Services/TransportationRepositories ```cs public class TransportationRepository : BaseRepository, ITransportationRepository { public TransportationRepository(ApplicationDBContext dbContext) : base(dbContext) { } public async Task> SearchTransportationsAsync( short? vehicleTypeId, int? fromCityId, int? toCityId, DateTime? startDate, DateTime? endDate) { var query = DbContext.Transportations .Include(x => x.Vehicle) .Include(x => x.FromLocation).ThenInclude(x => x.City) .Include(x => x.ToLocation).ThenInclude(x => x.City) .Include(x => x.Company) .AsQueryable(); query = query.Where(x => vehicleTypeId == null || x.Vehicle.VehicleTypeId == vehicleTypeId.Value); query = query.Where(x => fromCityId == null || x.FromLocation.CityId == fromCityId.Value); query = query.Where(x => toCityId == null || x.ToLocation.CityId == toCityId.Value); query = query.Where(x => startDate == null || x.StartDateTime.Date == startDate.Value.Date); query = query.Where(x => endDate == null || (x.EndDateTime.HasValue && x.EndDateTime.Value == endDate.Value.Date)); return await query.ToListAsync(); } } ``` # Auto Mapper Auto Mapper simplifies mapping between aggregates and DTOs in both directions. - [ ] Create a `MappingProfile` that inherits `Profile`, and use it to add configurations for mappings 📂 Suggested Folder: Application/Mappers/Profiles ```cs public class MappingProfile : Profile { public MappingProfile() { CreateMap() .ForMember(dest => dest.CompanyTitle, opt => opt.MapFrom(src => src.Company.Title)) .ForMember(dest => dest.FromLocationTitle, opt => opt.MapFrom(src => src.FromLocation.Title)) .ForMember(dest => dest.ToLocationTitle, opt => opt.MapFrom(src => src.ToLocation.Title)) .ForMember(dest => dest.FromCityTitle, opt => opt.MapFrom(src => src.FromLocation.City.Title)) .ForMember(dest => dest.ToCityTitle, opt => opt.MapFrom(src => src.ToLocation.City.Title)); CreateMap(); } } ``` - [ ] Register AutoMapper config file in `Program.cs` ```cs . . . builder.Services.AddAutoMapper(typeof(MappingProfile)); var app = builder.Build(); . . . ``` # Result & Result Status - [ ] Create `ResultStatus` enum and `Result` class 📂 Suggested Folder: Application/Result Result is a template to transfer data between services and controllers (in backend), so will use a generic type ```cs public class Result { public ResultStatus Status { get; set; } public string? ErrorMessage { get; set; } public T? Data { get; set; } public bool IsSuccess => Status == ResultStatus.Success; public static Result Success(T data) { return new Result { Status = ResultStatus.Success, Data = data }; } public static Result Error(T data) { return new Result { Status = ResultStatus.Error, Data = data }; } public static Result NotFound(T data) { return new Result { Status = ResultStatus.NotFound, Data = data }; } } ``` As you can see, there's a property of type ResultStatus, which is a enum for status of request ```cs public enum ResultStatus { Success, NotFound, ValidationError, Conflict, Unauthorized, Forbidden, Error } ``` You can read more about enums: [W3Schools](https://www.w3schools.com/cs/cs_enums.php) # `IService` & Service Now, use `I[Entity]Repositry` and `IUnitOfWork` in services to implement business logic - [ ] Create `I[Entity]Service` and `[Entity]Service` which implements it 📂 Suggested Folder for `I[Entity]Service`: Application/Interfaces 📂 Suggested Folder for Services: Application/Services - existence of an interface for each service class is optional - services can have multiple repositories in them -> logic-based structure An example of `I[Entity]Service`: ```c# public interface ITransportationService { Task>> SearchTransportationsAsync(TransportationSearchRequestDto searchRequest); } ``` An example of `[Entity]Service`: ```cs public class TransportationService : ITransportationService { private readonly ITransportationRepository _transportationRepository; private readonly IMapper _mapper; private readonly IUnitOfWork _unitOfWork; public TransportationService(ITransportationRepository transportationRepository, IMapper mapper, IUnitOfWork unitOfWork) { _transportationRepository = transportationRepository; _mapper = mapper; _unitOfWork = unitOfWork; } public async Task>> SearchTransportationsAsync(TransportationSearchRequestDto requestDto) { var result = await _transportationRepository.SearchTransportationsAsync( vehicleTypeId: requestDto.VehicleTypeId, fromCityId: requestDto.FromCityId, toCityId: requestDto.ToCityId, startDateTime: requestDto.StartDate, endDateTime: requestDto.EndDate); if (result.Any()) { var dto = _mapper.Map>(result); return Result>.Success(dto); } return Result>.NotFound(null); } } ``` - [ ] Register services in `Program.cs`: ```c# . . . builder.Services.AddScoped(); builder.Services.AddScoped(); . . . ``` # Controller Now we're getting to endpoints, you should communicate with client side through web-api. So every controller uses Services in Application layer to receive requests and send responses with DTOs. - [ ] Create an APIController (right click on the folder, and then under Add, select Controller, and then make sure to select the APIController type) 📂 Suggested Folder: WebAPI/Controller You should use ```[ApiController]``` attribute on top of them, route them and handle different status codes. TransportationController: ```cs [ApiController] [Route("api/[controller]")] public class TransportationController : ControllerBase { private readonly ITransportationService _transportationService; public TransportationController(ITransportationService transportationService) { _transportationService = transportationService; } [HttpGet("search")] public async Task SearchTransportations([FromQuery] TransportationSearchRequestDto searchRequest) { if (searchRequest == null) { return BadRequest("Invalid search request"); } var result = await _transportationService.SearchTransportationsAsync(searchRequest); if (result.IsSuccess) { return Ok(result); } // any unsuccessful status return result.Status switch { ResultStatus.NotFound => NotFound(result.ErrorMessage), ResultStatus.ValidationError => BadRequest(result.ErrorMessage), _ => StatusCode(500, result.ErrorMessage), }; } } ``` - HttpGet: handles a GET request from client -> important for routing - Ok, BadRequest, NotFound and StatusCode are Json results to send through api - Use TransportationService to communicate with Application # Inserting Sample Data For testing purposes, add some data into the related tables. You are provided with a SQL script, that adds some sample data into the following tables **Important Notes:** Note that different database names, and different table names will produce errors while executing the script. Consider adjusting these names before executing the script - Cities - Companies - LocationTypes - Locations - VehicleTypes - Vehicles - Transportation - [ ] Open `TransportationRelatedSampleData.sql` with SSMS, and execute the query # Merge - [ ] Create a PR and merge the current branch with develop