В представленном коде есть несколько проблем и возможностей для улучшения:
public bool BookTour(string userId, DateTime start, DateTime end, int hotelId, string roomType, string flightClass, int airCompanyId, int flightnumber)
В представленном коде есть несколько проблем и возможностей для улучшения:
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
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;
}
Ответ
В представленном коде есть несколько проблем и возможностей для улучшения:
- Парсинг 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);
}
}
Такой подход улучшает читаемость, безопасность и корректность работы с асинхронностью и исключениями.