Exemple #1
0
        private void SetUpForSucceededOrderHandle(DateTime sightseeingDate, IEnumerable <Ticket> tickets)
        {
            var info = new VisitInfo {
                MaxAllowedGroupSize = 3
            };
            var existentGroup = new SightseeingGroup {
                MaxGroupSize = info.MaxAllowedGroupSize, SightseeingDate = sightseeingDate, Tickets = new Ticket[] { _ticket }
            };
            var newGroup = new SightseeingGroup {
                MaxGroupSize = info.MaxAllowedGroupSize, SightseeingDate = sightseeingDate, Tickets = (ICollection <Ticket>)tickets
            };

            _infoDbServiceMock.Setup(x => x.GetAllAsync()).ReturnsAsync(new VisitInfo[] { info });

            _groupDbServiceMock.Setup(x => x.GetByAsync(It.IsAny <Expression <Func <SightseeingGroup, bool> > >())).ReturnsAsync(new SightseeingGroup[] { existentGroup });
            _groupDbServiceMock.Setup(x => x.RestrictedAddAsync(It.IsAny <SightseeingGroup>())).ReturnsAsync(newGroup);
            _groupDbServiceMock.Setup(x => x.RestrictedUpdateAsync(It.IsAny <SightseeingGroup>())).ReturnsAsync(existentGroup);

            _discountDbServiceMock.Setup(x => x.GetAsync(It.IsAny <string>())).ReturnsAsync(_discount);

            _ticketTariffDbServiceMock.Setup(x => x.GetAsync(It.IsAny <string>())).ReturnsAsync(_ticketTariff);

            // Only for CreateOrderAsync() purposes.
            _customerDbServiceMock.Setup(x => x.GetByAsync(It.IsAny <Expression <Func <Customer, bool> > >())).ReturnsAsync(new Customer[] { _customer });
        }
        /// <summary>
        /// Asynchronously adds <see cref="SightseeingGroup"/> entity. If <paramref name="isRestrict"/> set to false then no restrictions will be used. If set to true then the restricted mode will be used.
        /// It will check if in database is entity with the same 'SightseeingDate' value.
        /// </summary>
        /// <param name="group"><see cref="SightseeingGroup"/> to be added.</param>
        /// <param name="isRestrict">If set to false then no restrictions will be used and update allow entirely entity updating. If set to true then the restricted mode will be used.
        /// It will check if in database is entity with the same 'SightseeingDate' value.</param>
        /// <returns>Added <see cref="SightseeingGroup"/> entity.</returns>
        private async Task <SightseeingGroup> AddBaseAsync(SightseeingGroup group, bool isRestrict = false)
        {
            _logger.LogDebug($"Starting method '{nameof(AddBaseAsync)}'.");

            if (group is null)
            {
                throw new ArgumentNullException($"Argument '{nameof(group)}' cannot be null.");
            }

            await EnsureDatabaseCreatedAsync();

            _ = _context?.Groups ?? throw new InternalDbServiceException($"Table of type '{typeof(SightseeingGroup).Name}' is null.");

            try
            {
                if (isRestrict)
                {
                    // Resticted add mode that use custom equality comparer. The sightseeing tariffs are equal if they have the same SightseeingDate.

                    // Check if exist in db tariff with the same 'SightseeingDate' as adding.
                    if (await IsEntityAlreadyExistsAsync(group))
                    {
                        throw new InvalidOperationException($"There is already the same element in the database as the one to be added. The value of '{nameof(group.SightseeingDate)}' is not unique.");
                    }
                }
                else
                {
                    // Normal add mode without any additional restrictions.
                    if (_context.Groups.Contains(group))
                    {
                        throw new InvalidOperationException($"There is already the same element in the database as the one to be added. Id of this element: '{group.Id}'.");
                    }
                }

                _logger.LogDebug($"Starting add sightseeing group with id '{group.Id}'.");
                var addedGroup = _context.Groups.Add(group).Entity;
                await _context.TrySaveChangesAsync();

                _logger.LogDebug("Add data succeeded.");
                _logger.LogDebug($"Finished method '{nameof(AddBaseAsync)}'.");
                return(addedGroup);
            }
            catch (DbUpdateException ex)
            {
                _logger.LogError($"{ex.GetType().Name} - Changes made by add operations cannot be saved properly. See the inner exception for more details. Operation failed.", ex);
                var internalException = new InternalDbServiceException("Changes made by add operations cannot be saved properly. See the inner exception for more details.", ex);
                throw internalException;
            }
            catch (InvalidOperationException ex)
            {
                _logger.LogError($"{ex.GetType().Name} - {ex.Message}", ex);
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"{ex.GetType().Name} {ex.Message}");
                var internalException = new InternalDbServiceException($"Encountered problem when adding sighseeing group with id: '{group.Id}' to the database. See the inner excpetion for more details.", ex);
                throw internalException;
            }
        }
Exemple #3
0
        public void SetUp()
        {
            _groupDbServiceMock = new Mock <ISightseeingGroupDbService>();
            _infoDbServiceMock  = new Mock <IVisitInfoDbService>();

            _dbServiceFactoryMock = new Mock <IIndex <string, IServiceBase> >();
            _dbServiceFactoryMock.Setup(x => x["ISightseeingGroupDbService"]).Returns(_groupDbServiceMock.Object);
            _dbServiceFactoryMock.Setup(x => x["IVisitInfoDbService"]).Returns(_infoDbServiceMock.Object);

            _validSightseeingGroup = new SightseeingGroup
            {
                Id              = "1",
                MaxGroupSize    = 30,
                SightseeingDate = new DateTime(2019, 12, 12, 12, 0, 0)
            };
            _validSightseeingGroupDto = new SightseeingGroupDto
            {
                Id               = "1",
                MaxGroupSize     = 30,
                SightseeingDate  = new DateTime(2019, 12, 12, 12, 0, 0),
                IsAvailablePlace = true,
                CurrentGroupSize = 20
            };

            _info = CreateModel.CreateInfo();

            _logger = Mock.Of <ILogger <GroupsController> >();

            _mapperMock = new Mock <IMapper>();
            _mapperMock.Setup(x => x.Map <SightseeingGroupDto>(It.IsAny <SightseeingGroup>())).Returns(_validSightseeingGroupDto);
            _mapperMock.Setup(x => x.Map <SightseeingGroup>(It.IsAny <SightseeingGroupDto>())).Returns(_validSightseeingGroup);
        }
Exemple #4
0
        public void Validate__Sigthseeing_hour_is_not_full__Should_be_invalid()
        {
            var invalidGroup = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddDays(_maxDaysForOrder - 1).Date.AddHours(13).AddMinutes(23)
            };

            _validator.ShouldHaveValidationErrorFor(x => x.SightseeingDate, invalidGroup);
        }
Exemple #5
0
        public void Validate__MaxGroupSize_is_less_than_0__Should_be_invalid()
        {
            var invalidGroup = new SightseeingGroup {
                MaxGroupSize = -1
            };

            _validator.ShouldHaveValidationErrorFor(x => x.MaxGroupSize, invalidGroup);
        }
Exemple #6
0
        public void Validate__MaxGroupSize_is_between_0_and_max_allowed__Should_be_valid()
        {
            var validGroup = new SightseeingGroup {
                MaxGroupSize = MaxAllowedGroupSize - 1
            };

            _validator.ShouldNotHaveValidationErrorFor(x => x.MaxGroupSize, validGroup);
        }
Exemple #7
0
        public void Validate__MaxGroupSize_is_greater_than_max_allowed__Should_be_invalid()
        {
            var invalidGroup = new SightseeingGroup {
                MaxGroupSize = _info.MaxAllowedGroupSize + 1
            };

            _validator.ShouldHaveValidationErrorFor(x => x.MaxGroupSize, invalidGroup);
        }
Exemple #8
0
        public void Validate__MaxGroupSize_is_0_or_max_allowed__Should_be_valid([Values(0, MaxAllowedGroupSize)] int groupSize)
        {
            var validGroup = new SightseeingGroup {
                MaxGroupSize = groupSize
            };

            _validator.ShouldNotHaveValidationErrorFor(x => x.MaxGroupSize, validGroup);
        }
Exemple #9
0
        public void Validate__SightseeingDate_is_in_the_past__Should_be_invalid()
        {
            var invalidGroup = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddYears(-23)
            };

            _validator.ShouldHaveValidationErrorFor(x => x.SightseeingDate, invalidGroup);
        }
Exemple #10
0
        public void Validate__Sightseeing_hour_is_is_not_during_opening_hours__Should_be_invalid()
        {
            // DateTime is 15 minutes after closing.
            var invalidGroup = new SightseeingGroup {
                SightseeingDate = _info.GetClosingDateTime(DateTime.Now).AddMinutes(15)
            };

            _validator.ShouldHaveValidationErrorFor(x => x.SightseeingDate, invalidGroup);
        }
Exemple #11
0
        public void Validate__Sigthseeing_hour_full__Should_be_valid()
        {
            // SightseeingDate is set to 28 days since now, 13 p.m. It's valid because company is open between 10 and 18.
            var validGroup = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddDays(_maxDaysForOrder - 1).Date.AddHours(13)
            };

            _validator.ShouldNotHaveValidationErrorFor(x => x.SightseeingDate, validGroup);
        }
Exemple #12
0
        public void Validate__SightseeingDate_is_in_the_future_more_than_max_ticket_order_interval_specifies__Should_be_invalid()
        {
            // This max ticket order interval is 4 weeks by default.
            var invalidGroup = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddDays(_maxDaysForOrder + 1)
            };

            _validator.ShouldHaveValidationErrorFor(x => x.SightseeingDate, invalidGroup);
        }
        /// <summary>
        /// Asynchronously retrieves <see cref="SightseeingGroup"/> entities with specified page size and page number.
        /// Throws an exception if arguments is out of range or any problem with retrieving occurred.
        /// </summary>
        /// <param name="pageNumber">Page number that will be retrieved. Must be greater than 0.</param>
        /// <param name="pageSize">Page size. Must be a positive number.</param>
        /// <returns>Set of <see cref="SightseeingGroup"/> entities.</returns>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="pageSize"/> is a negative number or <paramref name="pageNumber"/> is less than 1.</exception>
        /// <exception cref="InternalDbServiceException">The resource does not exist or has a null value or any
        /// other problems with retrieving data from database occurred.</exception>
        public async Task <IEnumerable <SightseeingGroup> > GetWithPaginationAsync(int pageNumber = 1, int pageSize = 30)
        {
            _logger.LogInformation($"Starting method '{nameof(GetWithPaginationAsync)}'.");

            if (pageNumber < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(pageNumber), $"'{pageNumber}' is not valid value for argument '{nameof(pageNumber)}'. Only number greater or equal to 1 are valid.");
            }

            if (pageSize < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(pageSize), $"'{pageSize}' is not valid value for argument '{nameof(pageSize)}'. Only number greater or equal to 0 are valid.");
            }

            await EnsureDatabaseCreatedAsync();

            _ = _context?.Discounts ?? throw new InternalDbServiceException($"Table of type '{typeof(SightseeingGroup).Name}' is null.");

            try
            {
                IEnumerable <SightseeingGroup> groups = new SightseeingGroup[] { }.AsEnumerable();
                int maxNumberOfPageWithData;

                int numberOfResourceElements = await _context.Groups.CountAsync();

                int numberOfElementsOnLastPage = numberOfResourceElements % pageSize;
                int numberOfFullPages          = (numberOfResourceElements - numberOfElementsOnLastPage) / pageSize;

                if (numberOfElementsOnLastPage > 0)
                {
                    maxNumberOfPageWithData = ++numberOfFullPages;
                    _logger.LogWarning($"Last page of data contain {numberOfElementsOnLastPage} elements which is less than specified in {nameof(pageSize)}: {pageSize}.");
                }
                else
                {
                    maxNumberOfPageWithData = numberOfFullPages;
                }

                if (numberOfResourceElements == 0 || pageSize == 0 || pageNumber > maxNumberOfPageWithData)
                {
                    _logger.LogInformation($"Finished method '{nameof(GetWithPaginationAsync)}'. Returning {groups.Count()} elements.");
                    return(groups);
                }

                _logger.LogDebug($"Starting retrieve data. '{nameof(pageNumber)}': {pageNumber.ToString()}, '{nameof(pageSize)}': {pageSize.ToString()}.");
                groups = _context.Groups.Skip(pageSize * (pageNumber - 1)).Take(pageSize);
                _logger.LogDebug("Retrieve data succeeded.");
                _logger.LogInformation($"Finished method '{nameof(GetWithPaginationAsync)}'.");
                return(groups);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"{ex.GetType().Name} - {ex.Message}");
                var internalException = new InternalDbServiceException($"Encountered problem when retrieving sightseeing groups from the database. See the inner exception for more details.", ex);
                throw internalException;
            }
        }
Exemple #14
0
        public void Validate__SightseeingDate_is_in_the_future_less_than_max_ticket_order_interval__Should_be_valid()
        {
            // This max ticket order interval is 4 weeks by default.
            // SightseeingDate is set to 27:23:59:59 days since now, 13 p.m. It's valid because company is open between 10 and 18.
            var validGroup = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddDays(_maxDaysForOrder - 1).Date.AddHours(13)
            };

            _validator.ShouldNotHaveValidationErrorFor(x => x.SightseeingDate, validGroup);
        }
        public void Equals__Check_equality_the_same_single_discount__Should_be_the_same()
        {
            var sightseeingGroup1 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 8, 8), MaxGroupSize = 30
            };

            bool isEqual = sightseeingGroup1.Equals(sightseeingGroup1);

            isEqual.Should().BeTrue();
        }
Exemple #16
0
        public void Validate__SightseeingDate_is_in_the_future_equal_to_max_ticket_order_interval__Should_be_invalid()
        {
            // This max ticket order interval is 4 weeks by default.
            // SightseeingDate is set to 28 days since now, 13 p.m. It's valid because company is open between 10 and 18.
            var invalidGroup = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddDays(_maxDaysForOrder)
            };

            _validator.ShouldHaveValidationErrorFor(x => x.SightseeingDate, invalidGroup);
        }
        public void Equals__One_sightseeing_group_is_null__Should_not_be_the_same()
        {
            SightseeingGroup sightseeingGroup1 = null;
            var sightseeingGroup2 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 8, 8), MaxGroupSize = 30
            };

            bool isEqual = sightseeingGroup2.Equals(sightseeingGroup1);

            isEqual.Should().BeFalse();
        }
        public void Equals__One_sightseeing_group_is_reffered_to_second__Should_be_the_same()
        {
            var sightseeingGroup1 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 9, 9), MaxGroupSize = 30
            };
            var sightseeingGroup2 = sightseeingGroup1;

            bool isEqual = sightseeingGroup1.Equals(sightseeingGroup2);

            isEqual.Should().BeTrue();
        }
        public void Equals__Check_equality_of_two_different_types__Should_not_be_the_same()
        {
            DateTime?date = null;
            var      sightseeingGroup2 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 8, 8), MaxGroupSize = 30
            };

            bool isEqual = sightseeingGroup2.Equals(date);

            isEqual.Should().BeFalse();
        }
        public void Equals__Two_sightseeing_groups_with_the_same_properties_value__Should_be_the_same()
        {
            var sightseeingGroup1 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 9, 9), MaxGroupSize = 30
            };
            var sightseeingGroup2 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 9, 9), MaxGroupSize = 30
            };

            bool isEqual = sightseeingGroup1.Equals(sightseeingGroup2);

            isEqual.Should().BeTrue();
        }
        public void Equals__At_least_one_property_value_is_different__Should_not_be_the_same()
        {
            var sightseeingGroup1 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 9, 9), MaxGroupSize = 30
            };
            var sightseeingGroup2 = new SightseeingGroup {
                Id = "1", SightseeingDate = new DateTime(2019, 8, 8), MaxGroupSize = 30
            };

            bool isEqual = sightseeingGroup1.Equals(sightseeingGroup2);

            isEqual.Should().BeFalse();
        }
Exemple #22
0
        public void ValidFor__Group_is_set__Should_return_this_date_time()
        {
            var group = new SightseeingGroup {
                SightseeingDate = DateTime.Now.AddDays(2).Date.AddHours(14)
            };
            var ticket = new Ticket {
                Group = group
            };
            DateTime expectedDateTime = group.SightseeingDate;

            var validFor = ticket.ValidFor;

            validFor.Should().Be(expectedDateTime);
        }
Exemple #23
0
        private void SetUpForFailedUpdate(DateTime sightseeingDate)
        {
            var info = new VisitInfo {
                MaxAllowedGroupSize = 1
            };
            var existentGroup = new SightseeingGroup {
                MaxGroupSize = 1, SightseeingDate = sightseeingDate, Tickets = new Ticket[] { _ticket }
            };

            _infoDbServiceMock.Setup(x => x.GetAllAsync()).ReturnsAsync(new VisitInfo[] { info });

            _groupDbServiceMock.Setup(x => x.GetByAsync(It.IsAny <Expression <Func <SightseeingGroup, bool> > >())).ReturnsAsync(new SightseeingGroup[] { existentGroup });

            // Only for CreateOrderAsync() purposes.
            _customerDbServiceMock.Setup(x => x.GetByAsync(It.IsAny <Expression <Func <Customer, bool> > >())).ReturnsAsync(new Customer[] { _customer });
        }
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="InternalDbServiceException"></exception>
        /// <exception cref="ArgumentException"></exception>
        private async Task <SightseeingGroup> HandleNonexistentGroupAsync(VisitInfo recentInfo, DateTime sightseeingDate, IEnumerable <ShallowTicket> shallowTickets)
        {
            if (shallowTickets.Count() > recentInfo.MaxAllowedGroupSize)
            {
                throw new InvalidOperationException($"Attempt to add tickets to the group failed. There are '{recentInfo.MaxAllowedGroupSize}' available places on date '{sightseeingDate.ToString()}'.");
            }

            SightseeingGroup newGroup = null;

            try
            {
                newGroup = new SightseeingGroup
                {
                    MaxGroupSize    = recentInfo.MaxAllowedGroupSize,
                    SightseeingDate = sightseeingDate
                };

                // Check if new group based on order data is valid (SightseeingDate etc.).
                var validationResult = _groupValidator.Validate(newGroup);

                if (!validationResult.IsValid)
                {
                    throw new InvalidOperationException($"The group cannot be properly handled due to validation problems. {validationResult.Errors.First().ErrorMessage}");
                }

                var tickets = await CreateTicketsAsync(shallowTickets) as ICollection <Ticket>;

                AddCustomerToTickets(_customer, tickets);
                newGroup.Tickets = tickets;
                return(await _groupDbService.RestrictedAddAsync(newGroup));
            }
            catch (InvalidOperationException ex)
            {
                _logger.LogError(ex, ex.Message);
                throw;
            }
            catch (InternalDbServiceException ex)
            {
                _logger.LogError(ex, $"The group cannot be properly handled. Group sightseeing date: '{newGroup.SightseeingDate.ToString()}'. { ex.Message}");
                throw;
            }
        }
Exemple #25
0
        public async Task GetAvailableGroupDatesAsync__There_is_group_without_available_places__Returned_IEnumerable_should_not_contain_date_of_this_group()
        {
            _infoDbServiceMock.Setup(x => x.GetAllAsync()).ReturnsAsync(new VisitInfo[] { _info }.AsEnumerable());
            var notAvailableGroup = new SightseeingGroup {
                Id = "2", SightseeingDate = DateTime.Now.AddDays(1), MaxGroupSize = 1, Tickets = new Ticket[] { new Ticket {
                                                                                                                    Id = "only_for_this_test"
                                                                                                                } }
            };

            _groupDbServiceMock.Setup(x => x.GetByAsync(It.IsAny <Expression <Func <SightseeingGroup, bool> > >()))
            .ReturnsAsync(new SightseeingGroup[]
            {
                _validSightseeingGroup,
                notAvailableGroup
            }.AsEnumerable());
            var controller = new GroupsController(_dbServiceFactoryMock.Object, _logger, _mapperMock.Object);

            var result = await controller.GetAvailableGroupDatesAsync();

            (result as ObjectResult).StatusCode.Should().Be(200);
            (((result as ObjectResult).Value as ResponseWrapper).Data as IEnumerable <GroupInfo>).Any(x => x.SightseeingDate == notAvailableGroup.SightseeingDate).Should().BeFalse();
        }
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="InternalDbServiceException"></exception>
        private async Task <SightseeingGroup> HandleExistentGroupAsync(SightseeingGroup existedGroup, IEnumerable <ShallowTicket> shallowTickets)
        {
            int availablePlaces = existedGroup.MaxGroupSize - existedGroup.CurrentGroupSize;

            if (shallowTickets.Count() > availablePlaces)
            {
                throw new InvalidOperationException($"Attempt to add tickets to the group failed. There are '{availablePlaces}' place(s) " +
                                                    $"available on date '{existedGroup.SightseeingDate.ToString()}', but trying add '{shallowTickets.Count()}'.");
            }

            try
            {
                var tickets = await CreateTicketsAsync(shallowTickets) as ICollection <Ticket>;

                AddCustomerToTickets(_customer, tickets);

                // Update an existent group with new ordered tickets.


                var t = existedGroup.Tickets.ToList();
                t.AddRange(tickets);
                existedGroup.Tickets = t;


                return(await _groupDbService.RestrictedUpdateAsync(existedGroup));
            }
            catch (InvalidOperationException ex)
            {
                _logger.LogError(ex, $"The group cannot be properly handled. Group sightseeing date: '{existedGroup.SightseeingDate.ToString()}'. { ex.Message}");
                throw;
            }
            catch (InternalDbServiceException ex)
            {
                _logger.LogError(ex, $"Cannot saved data properly. Entity: '{nameof(SightseeingGroup)}'. Id: '{existedGroup.Id}'. {ex.Message}");
                throw;
            }
        }
        /// <summary>
        /// Asynchronously updates <see cref="SightseeingGroup"/> entity. If <paramref name="isRestrict"/> set to false then no restrictions will be used and update allow entirely entity updating.
        /// Otherwise the restricted mode will be using. It will ignore updating some read-only properties.
        /// </summary>
        /// <param name="group"><see cref="SightseeingGroup"/> to be updated.</param>
        /// <param name="isRestrict">If set to false then no restrictions will be used and update allow entirely entity updating. If set to true then the restricted mode will be used.
        /// It will ignore some read-only properties changes.</param>
        /// <returns>Updated <see cref="SightseeingGroup"/> entity.</returns>
        private async Task <SightseeingGroup> UpdateBaseAsync(SightseeingGroup group, bool isRestrict = false)
        {
            _logger.LogDebug($"Starting method '{nameof(UpdateBaseAsync)}'.");

            _ = group ?? throw new ArgumentNullException(nameof(group), $"Argument '{nameof(group)}' cannot be null.");

            if (string.IsNullOrEmpty(group.Id))
            {
                throw new ArgumentException($"Argument '{nameof(group.Id)}' cannot be null or empty.");
            }

            await EnsureDatabaseCreatedAsync();

            _ = _context?.Groups ?? throw new InternalDbServiceException($"Table of type '{typeof(SightseeingGroup).Name}' is null.");

            try
            {
                if (_context.Groups.Count() == 0)
                {
                    throw new InvalidOperationException($"Cannot found element with id '{group.Id}' for update. Resource {_context.Groups.GetType().Name} does not contain any element.");
                }

                if (await _context.Groups.ContainsAsync(group) == false)
                {
                    throw new InvalidOperationException($"Cannot found element with id '{group.Id}' for update. Any element does not match to the one to be updated.");
                }

                _logger.LogDebug($"Starting update sightseeing group with id '{group.Id}'.");

                SightseeingGroup updatedGroup = null;
                group.UpdatedAt = DateTime.UtcNow;

                if (isRestrict)
                {
                    // Resticted update mode that ignores all changes in read-only properties like Id, CreatedAt, UpdatedAt, ConcurrencyToken.
                    var originalGroup = await _context.Groups.SingleAsync(x => x.Id.Equals(group.Id));

                    updatedGroup = BasicRestrictedUpdate(originalGroup, group) as SightseeingGroup;
                }
                else
                {
                    // Normal update mode without any additional restrictions.
                    updatedGroup = _context.Groups.Update(group).Entity;
                }

                await _context.TrySaveChangesAsync();

                _logger.LogDebug($"Update data succeeded.");
                _logger.LogDebug($"Finished method '{nameof(UpdateBaseAsync)}'.");
                return(updatedGroup);
            }
            catch (InvalidOperationException ex)
            {
                _logger.LogError(ex, $"{ex.GetType().Name} - Cannot found element for update. See the exception for more details. Operation failed.");
                throw;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"{ex.GetType().Name} - {ex.Message}");
                var internalException = new InternalDbServiceException($"Encountered problem when updating sighseeing group with id '{group.Id}'. See the inner exception for more details.", ex);
                throw internalException;
            }
        }
 /// <summary>
 /// Asynchronously adds <see cref="SightseeingGroup"/> entity to the database. Throws an exception if
 /// already there is the same entity in database or any problem with saving changes occurred.
 /// </summary>
 /// <param name="group">The group to be added. Cannot be null.</param>
 /// <returns>The added entity.</returns>
 /// <exception cref="ArgumentNullException">The value of <paramref name="group"/> to be added is null.</exception>
 /// <exception cref="InvalidOperationException">There is the same entity that one to be added in database.</exception>
 /// <exception cref="InternalDbServiceException">The table with <see cref="SightseeingGroup"/> entities does not exist or it is null or
 /// cannot save properly any changes made by add operation.</exception>
 public async Task <SightseeingGroup> AddAsync(SightseeingGroup group)
 {
     _logger.LogInformation($"Starting method '{nameof(AddAsync)}'.");
     // Call normal add mode.
     return(await AddBaseAsync(group));
 }
 /// <summary>
 /// Asynchronously adds <see cref="SightseeingTariff"/> entity to the database. Do not allow add entity with the same SightseeingDate property.
 /// Throws an exception if already there is the same entity in database or any problem with saving changes occurred.
 /// </summary>
 /// <param name="group">The group to be added. Cannot be null.</param>
 /// <returns>The added entity.</returns>
 /// <exception cref="ArgumentNullException">The value of <paramref name="group"/> to be added is null.</exception>
 /// <exception cref="InvalidOperationException">There is the same entity that one to be added in database.</exception>
 /// <exception cref="InternalDbServiceException">The table with <see cref="SightseeingGroup"/> entities does not exist or it is null or
 /// cannot save properly any changes made by add operation.</exception>
 public async Task <SightseeingGroup> RestrictedAddAsync(SightseeingGroup group)
 {
     _logger.LogInformation($"Starting method '{nameof(RestrictedAddAsync)}'.");
     // Call restricted add mode.
     return(await AddBaseAsync(group, true));
 }
 private SightseeingGroupDto MapToDto(SightseeingGroup group) => _mapper.Map <SightseeingGroupDto>(group);