private Product MapToContract(ProductTableEntity productTableEntity) { return(new Product { Id = productTableEntity.Id, Name = productTableEntity.Name, Price = productTableEntity.Price, }); }
/// <summary> /// Event handler that listens for PrdouctChanged Events from the catalog service /// and synchronizes the Catalog Read Table in BasketEntity accordingly. /// Handles both productEntity inserts and updates. /// </summary> /// <param name="productEntity">ProductEntity Information</param> /// <param name="correlationId">Tracks request - can be any value</param> /// <returns></returns> public async Task ProductChanged(ProductEntity productEntity, string correlationId) { if (productEntity == null) { _logger.LogWarning($"Missing productEntity information for synchronization event in BasketEntity"); throw new Exception($"Missing productEntity information for synchronization event in BasketEntity"); } // Transform to ProductTableEntity objects var productTableEntity = new ProductTableEntity { PartitionKey = ProductPartitionKey, RowKey = productEntity.Id.ToString(), Title = productEntity.Title, ArtistName = productEntity.ArtistName, GenreName = productEntity.GenreName, Price = productEntity.Price.ToString(), //Upc = productEntity.Upc }; try { // Determine if record already exists var currentReadTableItems = await _productRepository.GetItem(ProductPartitionKey, productTableEntity.RowKey, correlationId); if (currentReadTableItems == null) { // Insert resource await _productRepository.Insert(productTableEntity, correlationId); _logger.LogInformation( $"Inserting ProductEntity {productEntity.Id}, {productTableEntity.Title} into Catalog ReadModel for Request {correlationId}"); } else { // Update resource await _productRepository.Update(productTableEntity, correlationId); _logger.LogInformation( $"Updating ProductEntity {productEntity.Id}, {productTableEntity.Title} to Catalog ReadModel for Request {correlationId}"); } } catch (Exception ex) { _logger.LogError(new EventId(ex.HResult), ex, "Exception throw in ProductChanged() in BasketEntity : {message}", ex.Message); throw new Exception($"Error in ProductChanged() in BasketEntity : {ex.Message}"); } await Task.CompletedTask; }
/// <summary> /// Add single line item to shopping basket /// </summary> /// <param name="productId">Id of productEntity to add</param> /// <param name="correlationToken">Tracks request - can be any value</param> /// <param name="basketId">Id of shopping basket</param> /// <returns>BasketItemEntity</returns> public async Task <BasketEntity> AddItemToBasket(int productId, string correlationToken, string basketId) { //* Materialized View Pattern //* Fetch product entity data from a read-only store contained in shopping basket service. //* ProductEntity ID is row key in underlying Azure table. //* Returns a ProductTableEntity class var productTableEntity = await _productRepository.GetItem(ProductPartitionKey, productId.ToString(), correlationToken); // Fallback logic if (productTableEntity == null) { // Fallback: // If product not available from local read store, fetch it from catalog service by // making direct HTTP call to Catalog Service. var product = await _restClient.GetAsync <ProductEntity>(ServiceEnum.Catalog, $"api/Catalog/Music/{productId}", correlationToken); if (product == null) { throw new Exception( $"Cannot add item to shopping basket: ProductEntity #{productId} does not exist for Request {correlationToken}. Have you created the ProductEntity Read Model for the Shopping BasketEntity microservice?"); } // Transform product into an entity class for table storage productTableEntity = new ProductTableEntity { // parition key is constant PartitionKey = ProductPartitionKey, // row key is productId RowKey = product.Data.Id.ToString(), Title = product.Data.Title, Id = product.Data.Id, GenreName = product.Data.GenreName, ArtistName = product.Data.ArtistName, Price = product.Data.Price.ToString() }; // Add product entity tolocal read store, implementing a cache-aside pattern await _productRepository.Insert(productTableEntity, correlationToken); _logger.LogInformation( $"Added productEntity information for item {productId} for Request {correlationToken} to the read model."); } BasketEntity basket = null; // Does basketID exist? if (basketId == "-1") { basketId = SnowflakeIdGenerator.GenerateId(SnowflakeEnum.Basket); } else { basket = await _distributedCacheRepository.GetBasketAsync(basketId, correlationToken, _telemetryClient); } // Get basket from cache if (basket == null) { // BasketEntity is null, add new single item to it basket = new BasketEntity { BuyerId = "2", BasketId = basketId, CorrelationToken = correlationToken, Items = { new BasketItemEntity { CorrelationToken = correlationToken, //DateCreated = DateTime.Now, Title = productTableEntity.Title, UnitPrice = productTableEntity.Price.ToString(), Artist = productTableEntity.ArtistName, Genre = productTableEntity.GenreName, Quantity = 1, ProductId = productId, } } }; basket = await _distributedCacheRepository.UpdateBasketAsync(basket, correlationToken, _telemetryClient); _logger.LogInformation($"Created new shopping basket {basketId} and added productEntity {productTableEntity.Title} for Request {correlationToken} "); } else { // BasketEntity is not null // Determine if the same productEntity has already been added to the basket var itemInBasket = basket.Items.FirstOrDefault(x => x.ProductId == Int32.Parse(productTableEntity.RowKey)); if (itemInBasket == null) { // ProductEntity does not exist in basket, add it basket.Items.Add(new BasketItemEntity() { CorrelationToken = correlationToken, //DateCreated = DateTime.Now, Title = productTableEntity.Title, UnitPrice = productTableEntity.Price.ToString(), Artist = productTableEntity.ArtistName, Genre = productTableEntity.GenreName, Quantity = 1, ProductId = Int32.Parse(productTableEntity.RowKey) }); _logger.LogInformation($"Added productEntity Id {productId} to shopping basket {basketId} for request {correlationToken}"); } else { // Idempotency write-check // Ensure that update with same correlation token does not already exist. // This could happen if we've already committed the write, but have gotten caught-up in retry logic. if (itemInBasket.CorrelationToken != null && itemInBasket.CorrelationToken == correlationToken) { _logger.LogWarning($"ProductEntity Id {productId} already added to shopping basket {basketId} for Request {correlationToken}"); return(basket); } // ProductEntity already exists in basket. Increment its count. basket.Items.FirstOrDefault(x => x.ProductId == Int32.Parse(productTableEntity.RowKey)).Quantity++; _logger.LogInformation($"Added productEntity Id {productId} to existing shopping basket {basketId} for request {correlationToken}"); } basket = await _distributedCacheRepository.UpdateBasketAsync(basket, correlationToken, _telemetryClient); } return(basket); }