private async Task MapCountryLocalities(string supplier, int countryId, string countryCode, string countryName, TelemetrySpan localityMappingSpan, CancellationToken cancellationToken) { _logger.LogMappingLocalitiesOfSpecifiedCountryStart(supplier, countryCode); var utcDate = DateTimeOffset.UtcNow; var changedLocalityPairs = new Dictionary <int, int>(); var localitiesToUpdate = new List <Locality>(); var newLocalities = new List <Locality>(); var normalizedCountryName = _locationNameNormalizer.GetNormalizedCountryName(countryName); var dbNormalizedLocalities = await _locationMapperDataRetrieveService.GetNormalizedLocalitiesByCountry(countryCode, cancellationToken); var notSuppliersLocalities = dbNormalizedLocalities .Where(l => !l.SupplierLocalityCodes.ContainsKey(supplier)).ToList(); var suppliersLocalities = dbNormalizedLocalities .Where(l => l.SupplierLocalityCodes.ContainsKey(supplier)).ToList(); var localities = await _context.RawAccommodations .Where(ac => ac.Supplier == supplier && ac.LocalityNames != null && ac.CountryCode == countryCode) .Select(ac => new { LocalityCode = ac.SupplierLocalityCode, LocalityNames = ac.LocalityNames }) .Distinct().ToListAsync(cancellationToken); localities = localities.GroupBy(l => _locationNameNormalizer.GetNormalizedLocalityName(normalizedCountryName, l.LocalityNames.En)) .Select(l => l.First()).ToList(); foreach (var locality in localities) { cancellationToken.ThrowIfCancellationRequested(); var defaultLocalityName = locality.LocalityNames.GetValueOrDefault(Constants.DefaultLanguageCode); var normalizedLocalityName = _locationNameNormalizer.GetNormalizedLocalityName(countryName, defaultLocalityName); if (!_localityValidator.IsNormalizedValid(normalizedCountryName, normalizedLocalityName)) { _logger.LogMappingInvalidLocality(defaultLocalityName, normalizedCountryName, supplier, JsonSerializer.Serialize(new { countryCode, countryId, countryName }), JsonSerializer.Serialize(locality)); continue; } var dbNotSuppliersLocality = notSuppliersLocalities.FirstOrDefault(l => l.Names.En == normalizedLocalityName); var dbSuppliersLocalities = suppliersLocalities.Where(l => l.Names.En == normalizedLocalityName).ToList(); var richLocality = new RichLocality(supplierCode: locality.LocalityCode, defaultNormalizedName: normalizedLocalityName, defaultNormalizedCountryName: normalizedCountryName, names: locality.LocalityNames); if (dbSuppliersLocalities.Any()) { ProcessIfSupplierLocalitiesExist(richLocality, dbSuppliersLocalities, dbNotSuppliersLocality); } else if (dbNotSuppliersLocality != default) { ProcessIfNotSupplierLocalityExists(richLocality, dbNotSuppliersLocality); } else { ProcessNewLocality(richLocality); } } _context.UpdateRange(localitiesToUpdate); _context.AddRange(newLocalities); await ChangeLocalityDependencies(changedLocalityPairs, cancellationToken); await _context.SaveChangesAsync(cancellationToken); await _localityChangePublisher.PublishRemovedLocalities(changedLocalityPairs.Keys.ToList()); await _localityChangePublisher.PublishAddedLocalities(newLocalities .Select(l => new LocalityData(l.Id, l.Names.En, countryName, countryCode)) .ToList()); _context.ChangeTracker.Entries() .Where(e => e.Entity != null) .Where(e => e.State != EntityState.Detached) .ToList() .ForEach(e => e.State = EntityState.Detached); localityMappingSpan.AddEvent($"Done mapping localities of country with code {countryCode}"); _logger.LogMappingLocalitiesOfSpecifiedCountryFinish(supplier, countryCode); void ProcessIfSupplierLocalitiesExist(RichLocality locality, List <Locality> supplierLocalities, Locality?notSupplierLocality) { var dbSuppliersLocality = supplierLocalities.First(); var dbSuppliersLocalitiesToDeactivate = supplierLocalities.GetRange(1, supplierLocalities.Count - 1); var names = _multilingualDataHelper.NormalizeLocalityMultilingualNames(locality.DefaultNormalizedCountryName, locality.Names); var supplierCodes = dbSuppliersLocality.SupplierLocalityCodes; foreach (var supplierLocality in dbSuppliersLocalitiesToDeactivate) { names = MultiLanguageHelpers.MergeMultilingualStrings(names, supplierLocality.Names); foreach (var supplierCode in supplierLocality.SupplierLocalityCodes) { supplierCodes.TryAdd(supplierCode.Key, supplierCode.Value); } supplierLocality.IsActive = false; supplierLocality.Modified = utcDate; localitiesToUpdate.Add(supplierLocality); changedLocalityPairs.Add(supplierLocality.Id, dbSuppliersLocality.Id); } if (notSupplierLocality != default) { names = MultiLanguageHelpers.MergeMultilingualStrings(names, notSupplierLocality.Names); changedLocalityPairs.Add(notSupplierLocality.Id, dbSuppliersLocality.Id); foreach (var sup in notSupplierLocality.SupplierLocalityCodes) { supplierCodes.TryAdd(sup.Key, sup.Value); } notSupplierLocality.IsActive = false; notSupplierLocality.Modified = utcDate; localitiesToUpdate.Add(notSupplierLocality); } names = _multilingualDataHelper.NormalizeLocalityMultilingualNames(locality.DefaultNormalizedCountryName, names); var dbLocality = new Locality { Id = dbSuppliersLocality.Id, CountryId = countryId, Names = names, SupplierLocalityCodes = supplierCodes, Modified = utcDate, IsActive = true }; localitiesToUpdate.Add(dbLocality); } void ProcessIfNotSupplierLocalityExists(RichLocality locality, Locality notSupplierLocality) { var supplierCodes = new Dictionary <string, string?>(notSupplierLocality.SupplierLocalityCodes); supplierCodes.TryAdd(supplier, locality.SupplierCode); var names = _multilingualDataHelper.NormalizeLocalityMultilingualNames(locality.DefaultNormalizedCountryName, MultiLanguageHelpers.MergeMultilingualStrings(locality.Names, notSupplierLocality.Names)); var dbLocality = new Locality { Id = notSupplierLocality !.Id, CountryId = countryId, Names = names, SupplierLocalityCodes = supplierCodes, IsActive = true, Modified = utcDate }; localitiesToUpdate.Add(dbLocality); } void ProcessNewLocality(RichLocality locality) { var dbLocality = new Locality { CountryId = countryId, Names = _multilingualDataHelper.NormalizeLocalityMultilingualNames(locality.DefaultNormalizedCountryName, locality.Names), IsActive = true, Modified = utcDate, Created = utcDate, SupplierLocalityCodes = new Dictionary <string, string?> { { supplier, locality.SupplierCode } } }; newLocalities.Add(dbLocality); } }