public bool BookTour(string userId, DateTime start, DateTime end, int hotelId, string roomType, string flightClass, int airCompanyId, int flightnumber) { try { //flightClass тоже не хотелось бы парсить enum var flightInfo = new FlightInfo(Enum.Parse<FlightClass>(flightClass), airCompanyId, flightnumber); var resultBookFlight = _airWaysService.Book(GetUser(userId), flightInfo); //roomType тоже не хотелось бы парсить enum var rentInfo = new RentInfo(start, end, hotelId, Enum.Parse<RoomType>(roomType)); //GetUserId в отдельная переменная var resultBookHotel = _hotelService.Book(GetUser(userId), rentInfo); //Exception уйдут в inner, не отловит Task.WhenAll(resultBookFlight, resultBookHotel); //Почему статикa reservationIdAir = resultBookFlight.Result; _reservationIdHotel = resultBookHotel.Result; } catch (BookFlightException) { _hotelService.CancelBook(_reservationIdAir); return false; } catch (BookHotelException) { _airWaysService.CancelBook(_reservationIdHotel); return false; } catch (Exception ex) { throw new ApplicationException(ex.Message); } finally { //поля 100% не инициализованы //Почему после exception мы пытаемся что-то сохранить // GetUser уже вынесли unitOfWork.Execute(async () => { await _tourRepository.Save(_reservationIdAir, _reservationIdHotel, GetUser(userId)); }).RunSynchronously(); //Interlock.Increment() _countBooked++; } return true; }
Ответ от нейросети
sobes.tech AI
В представленном коде есть несколько проблем и возможностей для улучшения:
-
Парсинг enum из строк —
Enum.Parse<FlightClass>(flightClass)иEnum.Parse<RoomType>(roomType)могут выбросить исключение, если строка невалидна. Лучше использоватьEnum.TryParseс обработкой ошибки или предусмотреть более безопасный способ передачи этих данных (например, напрямую enum или отдельный валидатор). -
Дублирование вызова
GetUser(userId)— лучше вызвать один раз и сохранить результат в переменную. -
Асинхронность и
Task.WhenAll— вызовTask.WhenAll(resultBookFlight, resultBookHotel);не дожидается завершения, так как не используетсяawait. Это значит, что ошибки могут быть не отловлены, а результат — не получен вовремя. -
Обработка исключений и отмена бронирования — в catch-блоках вызывается отмена брони, но параметры для отмены перепутаны (
_hotelService.CancelBook(_reservationIdAir);и наоборот). Это ошибка. -
Использование статических полей
_reservationIdAirи_reservationIdHotel— если метод вызывается параллельно, это приведёт к гонкам данных. Лучше использовать локальные переменные. -
В блоке finally вызывается асинхронный код через
RunSynchronously()— это плохая практика, может привести к дедлоку. Лучше сделать метод асинхронным и использоватьawait. -
Сохранение данных и инкремент счетчика бронирований происходит даже при исключениях — логично делать это только при успешном бронировании.
Пример исправленного варианта (упрощённо):
public async Task<bool> BookTourAsync(string userId, DateTime start, DateTime end, int hotelId, string roomType, string flightClass, int airCompanyId, int flightNumber)
{
var user = GetUser(userId);
if (!Enum.TryParse<FlightClass>(flightClass, out var parsedFlightClass))
return false; // или выбросить исключение
if (!Enum.TryParse<RoomType>(roomType, out var parsedRoomType))
return false;
var flightInfo = new FlightInfo(parsedFlightClass, airCompanyId, flightNumber);
var rentInfo = new RentInfo(start, end, hotelId, parsedRoomType);
string reservationIdAir = null;
string reservationIdHotel = null;
try
{
var bookFlightTask = _airWaysService.Book(user, flightInfo);
var bookHotelTask = _hotelService.Book(user, rentInfo);
await Task.WhenAll(bookFlightTask, bookHotelTask);
reservationIdAir = bookFlightTask.Result;
reservationIdHotel = bookHotelTask.Result;
await unitOfWork.Execute(async () =>
{
await _tourRepository.Save(reservationIdAir, reservationIdHotel, user);
});
Interlocked.Increment(ref _countBooked);
return true;
}
catch (BookFlightException)
{
if (reservationIdHotel != null)
await _hotelService.CancelBook(reservationIdHotel);
return false;
}
catch (BookHotelException)
{
if (reservationIdAir != null)
await _airWaysService.CancelBook(reservationIdAir);
return false;
}
catch (Exception ex)
{
throw new ApplicationException(ex.Message, ex);
}
}
Такой подход улучшает читаемость, безопасность и корректность работы с асинхронностью и исключениями.