stefanMinch3v

Expression tree parsing for factory fluent api

Sep 21st, 2020
808
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. namespace PetClinic.Domain.Appointments.Factories
  2. {
  3.     using Appointments.Models;
  4.     using Common.Exceptions;
  5.     using Common.SharedKernel;
  6.     using PetClinic.Domain.Common;
  7.     using System;
  8.     using System.Collections.Generic;
  9.     using System.Linq.Expressions;
  10.  
  11.     internal class AppointmentFactory : IAppointmentFactory
  12.     {
  13.         private readonly IClientFactory clientFactory;
  14.         private readonly IDoctorFactory doctorFactory;
  15.  
  16.         private AppointmentDate appointmentDate = default!;
  17.         private Client client = default!;
  18.         private Doctor doctor = default!;
  19.         private OfficeRoom officeRoom = default!;
  20.  
  21.         internal AppointmentFactory(
  22.             IClientFactory clientFactory,
  23.             IDoctorFactory doctorFactory)
  24.         {
  25.             this.clientFactory = clientFactory;
  26.             this.doctorFactory = doctorFactory;
  27.         }
  28.  
  29.         public Appointment Build()
  30.         {
  31.             if (this.appointmentDate is null
  32.                 || this.client is null
  33.                 || this.doctor is null
  34.                 || this.officeRoom is null)
  35.             {
  36.                 throw new InvalidAppointmentException("Invalid appointment input.");
  37.             }
  38.  
  39.             return new Appointment(this.doctor, this.client, this.appointmentDate, this.officeRoom);
  40.         }
  41.  
  42.         public IAppointmentFactory WithAppointmentDate(DateTime startDate, DateTime endDate)
  43.         {
  44.             this.appointmentDate = new AppointmentDate(startDate, endDate);
  45.             return this;
  46.         }
  47.  
  48.         public IAppointmentFactory WithClient(Expression<Action<IClientFactory>> clientExpression)
  49.         {
  50.             var dict = new Dictionary<string, string>();
  51.             this.ParseExpression(clientExpression, string.Empty, dict);
  52.  
  53.             this.client = this.clientFactory
  54.                 .WithName(dict[nameof(IClientFactory.WithName)])
  55.                 .WithUserId(dict[nameof(IClientFactory.WithUserId)])
  56.                 .Build();
  57.  
  58.             return this;
  59.         }
  60.  
  61.         public IAppointmentFactory WithDoctor(Expression<Action<IDoctorFactory>> doctorExpression)
  62.         {
  63.             var dict = new Dictionary<string, string>();
  64.             this.ParseExpression(doctorExpression, string.Empty, dict);
  65.  
  66.             this.doctor = this.doctorFactory
  67.                 .WithName(dict[nameof(IDoctorFactory.WithName)])
  68.                 .WithUserId(dict[nameof(IDoctorFactory.WithUserId)])
  69.                 .WithDoctorType(
  70.                     Enumeration.FromValue<DoctorType>(
  71.                         int.Parse(dict[nameof(IDoctorFactory.WithDoctorType)])))
  72.                 .Build();
  73.  
  74.             return this;
  75.         }
  76.  
  77.         public IAppointmentFactory WithOfficeRoom(int number, OfficeRoomType officeRoomType)
  78.         {
  79.             this.officeRoom = new OfficeRoom(false, number, officeRoomType);
  80.             return this;
  81.         }
  82.  
  83.         private void ParseExpression(Expression expression, string key, IDictionary<string, string> dict)
  84.         {
  85.             // expression starts here in type lambda
  86.             if (expression.NodeType == ExpressionType.Lambda)
  87.             {
  88.                 var lambdaExpression = (LambdaExpression)expression;
  89.                 foreach (var parameter in lambdaExpression.Parameters)
  90.                 {
  91.                     ParseExpression(parameter, string.Empty, dict);
  92.                 }
  93.  
  94.                 var body = lambdaExpression.Body;
  95.                 ParseExpression(body, string.Empty, dict);
  96.             }
  97.             // here we get the actual parameter of the method
  98.             else if (expression.NodeType == ExpressionType.Constant)
  99.             {
  100.                 var constantExpression = (ConstantExpression)expression;
  101.                 var value = constantExpression.Value;
  102.  
  103.                 // set the method value: for instance WithName("Gosho") so we add Gosho
  104.                 dict[key] = value.ToString()!;
  105.             }
  106.             // here we get the name of the method
  107.             else if (expression.NodeType == ExpressionType.Call)
  108.             {
  109.                 var methodCallExpression = (MethodCallExpression)expression;
  110.  
  111.                 // set the method name: for instance WithName
  112.                 if (!dict.ContainsKey(methodCallExpression.Method.Name))
  113.                 {
  114.                     dict.Add(methodCallExpression.Method.Name, string.Empty);
  115.                 }
  116.  
  117.                 ParseExpression(methodCallExpression.Object, methodCallExpression.Method.Name, dict);
  118.  
  119.                 foreach (var argument in methodCallExpression.Arguments)
  120.                 {
  121.                     ParseExpression(argument, methodCallExpression.Method.Name, dict);
  122.                 }
  123.             }
  124.         }
  125.     }
  126. }
  127.  
  128. namespace PetClinic.Application.Appointments.Commands.MakeAsDoctor
  129. {
  130.     using Common;
  131.     using Common.Contracts;
  132.     using Domain.Appointments.Factories;
  133.     using Domain.Appointments.Models;
  134.     using Domain.Common;
  135.     using Domain.Common.SharedKernel;
  136.     using MediatR;
  137.     using System;
  138.     using System.Threading;
  139.     using System.Threading.Tasks;
  140.  
  141.     public class MakeAsDoctorAppointmentCommand : IRequest<Result>
  142.     {
  143.         public string UserIdClient { get; set; } = default!;
  144.         public string Name { get; set; } = default!;
  145.         public int DoctorType { get; set; }
  146.         public int RoomNumber { get; set; }
  147.         public int OfficeRoomType { get; set; }
  148.         public DateTime StartDate { get; set; }
  149.         public DateTime EndDate { get; set; }
  150.  
  151.         public class MakeAsDoctorAppointmentCommandHandler : IRequestHandler<MakeAsDoctorAppointmentCommand, Result>
  152.         {
  153.             private readonly IAppointmentRepository appointmentRepository;
  154.             private readonly ICurrentUser currentUser;
  155.             private readonly IAppointmentFactory appointmentFactory;
  156.  
  157.             public MakeAsDoctorAppointmentCommandHandler(
  158.                 IAppointmentRepository appointmentRepository,
  159.                 ICurrentUser currentUser,
  160.                 IAppointmentFactory appointmentFactory)
  161.             {
  162.                 this.appointmentRepository = appointmentRepository;
  163.                 this.currentUser = currentUser;
  164.                 this.appointmentFactory = appointmentFactory;
  165.             }
  166.  
  167.             public async Task<Result> Handle(MakeAsDoctorAppointmentCommand request, CancellationToken cancellationToken)
  168.             {
  169.                 var isDateAvailable = await this.appointmentRepository.IsDateAvailable(
  170.                     this.currentUser.UserId,
  171.                     request.RoomNumber,
  172.                     request.StartDate,
  173.                     request.EndDate,
  174.                     cancellationToken);
  175.  
  176.                 if (!isDateAvailable)
  177.                 {
  178.                     return "The chosen date is not available.";
  179.                 }
  180.  
  181.                 var appointment = this.appointmentFactory
  182.                     .WithDoctor(doctor => doctor
  183.                         .WithDoctorType(Enumeration.FromValue<DoctorType>(request.DoctorType))
  184.                         .WithName(this.currentUser.UserName)
  185.                         .WithUserId(this.currentUser.UserId)
  186.                         .Build())
  187.                     .WithClient(client => client
  188.                         .WithName(request.Name)
  189.                         .WithUserId(request.UserIdClient)
  190.                         .Build())
  191.                     .WithOfficeRoom(request.RoomNumber, Enumeration.FromValue<OfficeRoomType>(request.OfficeRoomType))
  192.                     .WithAppointmentDate(request.StartDate, request.EndDate)
  193.                     .Build();
  194.  
  195.                 await this.appointmentRepository.Save(appointment, cancellationToken);
  196.  
  197.                 return Result.Success;
  198.             }
  199.         }
  200.     }
  201. }
RAW Paste Data