public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent @event)
        {
            using (LogContext.PushProperty("IntegrationEventContext", $"{@event.Id}-{Program.AppName}"))
            {
                _logger.LogInformation("----- Handling integration event: {IntegrationEventId} at {AppName} - ({@IntegrationEvent})", @event.Id, Program.AppName, @event);

                var confirmedOrderStockItems = new List <ConfirmedOrderStockItem>();

                foreach (var orderStockItem in @event.OrderStockItems)
                {
                    var catalogItem             = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
                    var hasStock                = catalogItem.AvailableStock >= orderStockItem.Units;
                    var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);

                    confirmedOrderStockItems.Add(confirmedOrderStockItem);
                }

                var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
                    ? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems)
                    : new OrderStockConfirmedIntegrationEvent(@event.OrderId);

                await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);

                await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
            }
        }
        public async Task <ActionResult> UpdateProductAsync([FromBody] CatalogItem productToUpdate)
        {
            var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);

            if (catalogItem == null)
            {
                return(NotFound(new { Message = $"Item with id {productToUpdate.Id} not found." }));
            }

            var oldPrice = catalogItem.Price;
            var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price;

            // Update current product
            catalogItem = productToUpdate;
            _catalogContext.CatalogItems.Update(catalogItem);

            if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed
            {
                //Create Integration Event to be published through the Event Bus
                var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);

                // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
                await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent);

                // Publish through the Event Bus and mark the saved event as published
                await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent);
            }
            else // Just save the updated product because the Product's Price hasn't changed.
            {
                await _catalogContext.SaveChangesAsync();
            }

            return(CreatedAtAction(nameof(ItemByIdAsync), new { id = productToUpdate.Id }, null));
        }
Example #3
0
        public async Task <ActionResult> UpdateOfferAsync([FromBody] Offer offerToUpdate)
        {
            var offerItem = await _catalogContext.Offers.SingleOrDefaultAsync(i => i.Id == offerToUpdate.Id);

            if (offerItem == null)
            {
                return(NotFound(new { Messsage = $"Items with id {offerToUpdate.Id} not found." }));
            }

            var oldOfferPercent = offerItem.PercentOffer;
            var raiseOfferPercentChangedEvent = oldOfferPercent != offerToUpdate.PercentOffer;

            offerItem = offerToUpdate;
            _catalogContext.Offers.Update(offerItem);

            if (raiseOfferPercentChangedEvent)
            {
                var offerPercentChangeEvent = new OfferPercentChangedIntegrationEvent(offerItem.Id, offerToUpdate.PercentOffer, oldOfferPercent);
                await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(offerPercentChangeEvent);

                await _catalogIntegrationEventService.PublishThroughEventBusAsync(offerPercentChangeEvent);
            }
            else
            {
                await _catalogContext.SaveChangesAsync();
            }

            return(CreatedAtAction(nameof(OfferByIdAsync), new { id = offerToUpdate.Id }, null));
        }
Example #4
0
        public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem productToUpdate)
        {
            var catalogItem = await _catalogContext.CatalogItems.SingleOrDefaultAsync(i => i.Id == productToUpdate.Id);

            if (catalogItem == null)
            {
                return(NotFound());
            }
            var raiseProductPriceChangedEvent = catalogItem.Price != productToUpdate.Price;
            var oldPrice = catalogItem.Price;

            // Update current product
            catalogItem = productToUpdate;
            _catalogContext.CatalogItems.Update(catalogItem);

            if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed
            {
                //Create Integration Event to be published through the Event Bus
                var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);

                // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
                await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent);

                // Publish through the Event Bus and mark the saved event as published
                await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent);
            }
            else // Save updated product
            {
                await _catalogContext.SaveChangesAsync();
            }

            return(Ok());
        }
Example #5
0
        public async Task <IActionResult> UpdateProduct([FromBody] CatalogItem productToUpdate)
        {
            var catalogItem = await _catalogContext.CatalogItems
                              .SingleOrDefaultAsync(c => c.Id == productToUpdate.Id);

            if (null == catalogItem)
            {
                return(NotFound(new { Message = $"未找到ID为{productToUpdate.Id}的目录项" }));
            }

            var oldPrice = catalogItem.Price;
            var raiseProductPriceChangedEvent = oldPrice != productToUpdate.Price;

            //更新当前产品
            catalogItem = productToUpdate;
            _catalogContext.CatalogItems.Update(catalogItem);
            //价格改变,保存和发布事件。
            if (raiseProductPriceChangedEvent)
            {
                //创建价格改变事件
                var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);
                //保存目录数据和事件发布日志--此步骤需要通过本地事务保证原子性。
                await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent);

                //发布事件
                await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent);
            }
            else
            {
                await _catalogContext.SaveChangesAsync();
            }

            return(CreatedAtAction(nameof(GetItemById), new { id = productToUpdate.Id }, null));
        }
Example #6
0
        public async Task UpdateProductAsync(CatalogItem productToUpdate)
        {
            //TODO: checking for necessity of publishing event PriceChanged
            //TODO: Other possibilities for state of event is not considered
            //To update the price of a product and guarantee consistency,
            //(that the publishing of it's related event has definitely happened after persisting in current service,
            //first we need to update CatalogContext(and product)
            //and we need to store details of the related event in database (IntegrationEventLogContext)
            //IN THE SAME TRANSACTION
            //then we will publish the event
            //and if everything goes as expected, we change the state of event to published in db
            //TODO don't send productToUpdate to repo. fetch the current object, change it's price then send it (if fetched in admin panel before update there is no problem
            var oldPrice = _catalogContext.CatalogItems.AsNoTracking().Single(e => e.Id == productToUpdate.Id).Price;

            if (oldPrice != productToUpdate.Price)
            {
                ProductPriceChangedIntegrationEvent evt =
                    new ProductPriceChangedIntegrationEvent(productToUpdate.Id, productToUpdate.Price, oldPrice);
                using (var transaction = _catalogContext.Database.BeginTransaction())
                {
                    _catalogContext.CatalogItems.Update(productToUpdate);
                    await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(evt);

                    transaction.Commit();
                }

                _eventBus.Publish(evt);
                await _integrationEventLogService.MarkEventAsPublished(evt.Id);
            }
            else
            {
                _catalogContext.CatalogItems.Update(productToUpdate);
                _catalogContext.SaveChanges();
            }
        }
        public async Task <ActionResult> UpdateOfferAsync([FromBody] Book bookToUpdate)
        {
            var bookItem = await _catalogContext.Books.SingleOrDefaultAsync(i => i.Id == bookToUpdate.Id);

            if (bookItem == null)
            {
                return(NotFound(new { Messsage = $"Book with id {bookToUpdate.Id} not found." }));
            }

            var oldPrice = bookItem.Price;
            var raiseOfPriceChangedEvent = oldPrice != bookToUpdate.Price;

            bookItem = bookToUpdate;
            _catalogContext.Books.Update(bookItem);

            if (raiseOfPriceChangedEvent)
            {
                var offerPercentChangeEvent = new BookPriceChangedIntegrationEvent(bookToUpdate.Id, bookToUpdate.Price, oldPrice);
                await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(offerPercentChangeEvent);

                await _catalogIntegrationEventService.PublishThroughEventBusAsync(offerPercentChangeEvent);
            }
            else
            {
                await _catalogContext.SaveChangesAsync();
            }

            return(CreatedAtAction(nameof(BookByIdAsync), new { id = bookToUpdate.Id }, null));
        }
        public async Task Handle(OrderStatusChangedToAwaitingValidationIntegrationEvent command)
        {
            var confirmedOrderStockItems = new List <ConfirmedOrderStockItem>();

            foreach (var orderStockItem in command.OrderStockItems)
            {
                var catalogItem             = _catalogContext.Products.Find(orderStockItem.ProductId);
                var hasStock                = catalogItem.AvailableStock >= orderStockItem.Units;
                var confirmedOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);

                confirmedOrderStockItems.Add(confirmedOrderStockItem);
            }

            var confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => !c.HasStock)
                ? (IntegrationEvent) new OrderStockRejectedIntegrationEvent(command.OrderId, confirmedOrderStockItems)
                : new OrderStockConfirmedIntegrationEvent(command.OrderId);
            var status = confirmedOrderStockItems.Any(c => !c.HasStock)?"Order Stock Confirmed":"Order Stock Rejected";
            await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(command.OrderId, status, confirmedIntegrationEvent);

            await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);
        }
        public async Task HandleAsync(OrderStatusChangedToAwaitingValidationIntegrationEvent @event)
        {
            var confirmedOrderStockItems = new List <ConfirmedOrderStockItem>();

            foreach (var orderStockItem in @event.OrderStockItems)
            {
                var catalogItem            = _catalogContext.CatalogItems.Find(orderStockItem.ProductId);
                var hasStock               = catalogItem.AvailableStock >= orderStockItem.Units;
                var confiredOrderStockItem = new ConfirmedOrderStockItem(catalogItem.Id, hasStock);

                confirmedOrderStockItems.Add(confiredOrderStockItem);
            }

            IntegrationEvent confirmedIntegrationEvent = confirmedOrderStockItems.Any(c => c.HasStock)
                ? new OrderStockConfirmedIntegrationEvent(@event.OrderId) as IntegrationEvent
                : new OrderStockRejectedIntegrationEvent(@event.OrderId, confirmedOrderStockItems) as IntegrationEvent;

            await _catalogIntegrationEventService.PublishThroughEventBusAsync(confirmedIntegrationEvent);

            await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(confirmedIntegrationEvent);
        }
        public async Task <IActionResult> UpdateProductAsync(string id, [FromBody] ProductViewModel updateModel,
                                                             CancellationToken cancellationToken)
        {
            try
            {
                var item = await _catalogContext.Products
                           .Include(x => x.ProductImages)
                           .Include(x => x.ProductColors)
                           .SingleOrDefaultAsync(i => i.Id == id, cancellationToken);

                if (item == null)
                {
                    return(NotFound(new { Message = $"Item with id {updateModel.Id} not found." }));
                }

                if (updateModel.CategoryId <= 0 || updateModel.ManufacturerId <= 0)
                {
                    return(BadRequest(new
                    {
                        Message = $"Cant update Product when category and manufacturer is not assign."
                    }));
                }
                if (updateModel.ProductImages == null || updateModel.ProductImages.Length <= 0)
                {
                    return(BadRequest(new { Message = "Cant update product with 0 image" }));
                }

                var oldPrice = item.Price;
                var raiseProductPriceChangedEvent = oldPrice != updateModel.Price;

                updateModel.Id = id;

                #region Clean all Images and colors

                _catalogContext.ProductImages.RemoveRange(item.ProductImages);
                _catalogContext.ProductColors.RemoveRange(item.ProductColors);

                // Delete Previous Images
                foreach (var image in item.ProductImages)
                {
                    _fileUtility.DeleteFile("ProductImage/" + id, image.ImageName);
                }

                // Insert new images
                foreach (var image in updateModel.ProductImages)
                {
                    await InsertProductImageAsync(updateModel, cancellationToken, image);
                }

                await _catalogContext.SaveChangesAsync(cancellationToken);

                #endregion

                #region Mapping
                // Update current product
                item.Description    = updateModel.Description;
                item.LastUpdatedBy  = updateModel.ActorId;
                item.LastUpdated    = DateTime.Now;
                item.Name           = updateModel.Name;
                item.CategoryId     = updateModel.CategoryId;
                item.ManufacturerId = updateModel.ManufacturerId;
                item.Price          = updateModel.Price;
                item.ProductImages  = updateModel.ProductImages.Select(x =>
                                                                       new ProductImage {
                    ImageName = x.ImageName, ImageUrl = x.ImageUrl, ProductId = x.ProductId
                })
                                      .ToList();
                item.ProductColors = updateModel.ProductColors
                                     .Select(x => new ProductColor {
                    Name = x.Name, ProductId = x.ProductId
                }).ToList();

                #endregion

                // Update current product
                _catalogContext.Products.Update(item);


                if (raiseProductPriceChangedEvent) // Save product's data and publish integration event through the Event Bus if price has changed
                {
                    //Create Integration Event to be published through the Event Bus
                    var priceChangedEvent = new ProductPriceChangedIntegrationEvent(item.Id, updateModel.Price, oldPrice);

                    // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
                    await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(0, "PriceChanged", priceChangedEvent);

                    // Publish through the Event Bus and mark the saved event as published
                    await _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent);
                }
                else // Just save the updated product because the Product's Price hasn't changed.
                {
                    await _catalogContext.SaveChangesAsync(cancellationToken);
                }

                return(CreatedAtAction(nameof(UpdateProductAsync), new
                {
                    id = item.Id
                }, null));
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }