private bool DeleteProducts( IReadOnlyCollection <ProductCatalog> productCatalogs, ICatalogPublisher catalogPublisher) { Stopwatch watchTotalDelete = Stopwatch.StartNew(); bool changesDetected = false; //// 1: delete listings for the catalogs which are no longer exist in AX (were retracted for instance). //// This is the one of 2 fastest steps (because we don't need to query CRT for each product to figure out whether it is still there or not) DataAccessor dataAccessor = new DataAccessor(this.onlineChannel.RecordId, this.runtime.Configuration.ConnectionString, this.publishingConfig.CRTListingPageSize); Stopwatch watch = Stopwatch.StartNew(); Dictionary <string, List <long> > catalogsToBeDeleted = dataAccessor.GetNotExistingCatalogs(productCatalogs); watch.Stop(); this.LogTimingMessage(Resources.Duration_GetNotExistingCatalogs, watch.Elapsed, catalogsToBeDeleted.Count, catalogsToBeDeleted.Values.Count); if (catalogsToBeDeleted.Count > 0) { watch.Restart(); catalogPublisher.OnDeleteProductsByCatalogIdRequested(catalogsToBeDeleted); watch.Stop(); this.LogTimingMessage(Resources.Duration_Processor_DeleteListingsByCatalogs, watch.Elapsed); } watch.Restart(); dataAccessor.DeleteListingsByCatalogs(catalogsToBeDeleted.SelectMany(c => c.Value)); watch.Stop(); this.LogTimingMessage(Resources.Duration_Manager_DeleteListingsByCatalogs, watch.Elapsed); // 2. delete listings for languages which are no longer exist on channel, this is another fast operation (in terms of querying CRT). watch.Restart(); Dictionary <string, List <string> > languagesToBeDeleted = dataAccessor.GetNotExistingLanguages(this.ChannelLanguages); watch.Stop(); this.LogTimingMessage(Resources.Duration_GetNotExistingLanguages, watch.Elapsed, languagesToBeDeleted.Keys.Count, languagesToBeDeleted.Values.Count); if (languagesToBeDeleted.Count > 0) { watch.Restart(); catalogPublisher.OnDeleteProductsByLanguageIdRequested(languagesToBeDeleted); watch.Stop(); this.LogTimingMessage(Resources.Duration_Processor_DeleteListingsByLanguage, watch.Elapsed); } if (languagesToBeDeleted.Count > 0) { watch.Restart(); dataAccessor.DeleteListingsByLanguages(languagesToBeDeleted.SelectMany(c => c.Value)); watch.Stop(); this.LogTimingMessage(Resources.Duration_Processor_DeleteListingsByLanguages, watch.Elapsed); } changesDetected |= (catalogsToBeDeleted.Count > 0) || (languagesToBeDeleted.Count > 0); if (this.publishingConfig.CheckEveryListingForRemoval) { // 3: Finally read all listings left from published listings table and ask CRT whehter the product still available there or not watch.Restart(); Dictionary <long, List <ListingIdentity> > catalogs = dataAccessor.LoadAllListingsMap(); watch.Stop(); int listingsCount = 0; foreach (List <ListingIdentity> list in catalogs.Values) { listingsCount += list.Count; } this.LogTimingMessage(Resources.Duration_LoadListingsMap, watch.Elapsed, listingsCount, catalogs.Keys.Count); // Loop over published listings which are grouped by a catalog foreach (KeyValuePair <long, List <ListingIdentity> > catalog in catalogs) { int bottomIndex = 0; List <ListingIdentity> publishedIds = catalog.Value; // Calling CRT, in a separate pages, to find out whether the products are still available or not. while (bottomIndex < publishedIds.Count) { int topIndex = bottomIndex + this.publishingConfig.CRTListingPageSize - 1; if (topIndex + 1 >= publishedIds.Count) { topIndex = publishedIds.Count - 1; } List <ListingIdentity> currentPagePublishedIds = publishedIds.GetRange(bottomIndex, topIndex + 1 - bottomIndex); int previousBottomIndex = bottomIndex; bottomIndex = topIndex + 1; watch.Restart(); System.Collections.ObjectModel.ReadOnlyCollection <ProductExistenceId> crtIds = this.VerifyProductExistence(catalog.Key, ConvertToProductExistenceId(currentPagePublishedIds)); watch.Stop(); this.LogTimingMessage(Resources.Duration_VerifyProductExistence, watch.Elapsed, currentPagePublishedIds.Count, previousBottomIndex, publishedIds.Count, crtIds.Count); IList <ListingIdentity> idsToBeRemoved = GetIdsToBeRemoved(crtIds, currentPagePublishedIds); changesDetected |= idsToBeRemoved.Any(); if (idsToBeRemoved.Count > 0) { watch.Restart(); catalogPublisher.OnDeleteIndividualProductsRequested(idsToBeRemoved); watch.Stop(); this.LogTimingMessage(Resources.Duration_Processor_DeleteListingsByCompositeIds, watch.Elapsed, idsToBeRemoved.Count); watch.Restart(); dataAccessor.DeleteListingsByCompositeIds(catalog.Key, idsToBeRemoved); watch.Stop(); this.LogTimingMessage(Resources.Duration_Manager_DeleteListingsByCompositeIds, watch.Elapsed, idsToBeRemoved.Count); } List <ListingPublishStatus> statuses = new List <ListingPublishStatus>(); foreach (ListingIdentity id in idsToBeRemoved) { statuses.Add(Listing.CreateStatusSuccessfullyDeleted(this.onlineChannel.RecordId, id.CatalogId, id.ProductId, id.LanguageId)); } if (statuses.Count > 0) { this.UpdateListingPublishingStatus(statuses); } } } watchTotalDelete.Stop(); this.LogTimingMessage(Resources.Duration_DeleteProducts, watchTotalDelete.Elapsed, changesDetected); } return(changesDetected); }