From fd76f390ef21da007ec018f09004f35c8c1752f7 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 13 Jun 2023 11:31:11 -0500 Subject: [PATCH 1/3] Cache JsonSerializerOptions According to https://github.com/dotnet/runtime/issues/65396, using a new JsonSerializerOptions every time JsonSerializer is invoked is a suboptimal pattern. Fix this by caching JsonSerializerOptions instances. --- .../aggregator/Services/OrderApiClient.cs | 5 +---- .../aggregator/Services/OrderApiClient.cs | 5 +---- .../EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs | 10 +++++----- .../IntegrationEventLogEntry.cs | 10 +++++----- .../Repositories/RedisBasketRepository.cs | 9 +++------ src/Services/Services.Common/JsonHelper.cs | 12 ++++++++++++ src/Web/WebMVC/Services/BasketService.cs | 15 +++------------ src/Web/WebMVC/Services/CatalogService.cs | 5 +---- src/Web/WebMVC/Services/OrderingService.cs | 10 ++-------- src/Web/WebhookClient/Services/WebhooksClient.cs | 5 +---- 10 files changed, 34 insertions(+), 52 deletions(-) create mode 100644 src/Services/Services.Common/JsonHelper.cs diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs index 6a8465df6..19f47e4b6 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -23,9 +23,6 @@ public class OrderApiClient : IOrderApiClient var ordersDraftResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(ordersDraftResponse, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + return JsonSerializer.Deserialize(ordersDraftResponse, JsonHelper.CaseInsensitiveOptions); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs index 53fb65bb8..4ff678b63 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -23,9 +23,6 @@ public class OrderApiClient : IOrderApiClient var ordersDraftResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(ordersDraftResponse, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + return JsonSerializer.Deserialize(ordersDraftResponse, JsonHelper.CaseInsensitiveOptions); } } diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs index aa4dc17ef..d27f775e8 100644 --- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs +++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs @@ -5,6 +5,9 @@ public class EventBusRabbitMQ : IEventBus, IDisposable { const string BROKER_NAME = "eshop_event_bus"; + private static readonly JsonSerializerOptions s_indentedOptions = new() { WriteIndented = true }; + private static readonly JsonSerializerOptions s_caseInsensitiveOptions = new() { PropertyNameCaseInsensitive = true }; + private readonly IRabbitMQPersistentConnection _persistentConnection; private readonly ILogger _logger; private readonly IEventBusSubscriptionsManager _subsManager; @@ -69,10 +72,7 @@ public class EventBusRabbitMQ : IEventBus, IDisposable channel.ExchangeDeclare(exchange: BROKER_NAME, type: "direct"); - var body = JsonSerializer.SerializeToUtf8Bytes(@event, @event.GetType(), new JsonSerializerOptions - { - WriteIndented = true - }); + var body = JsonSerializer.SerializeToUtf8Bytes(@event, @event.GetType(), s_indentedOptions); policy.Execute(() => { @@ -256,7 +256,7 @@ public class EventBusRabbitMQ : IEventBus, IDisposable var handler = scope.ServiceProvider.GetService(subscription.HandlerType); if (handler == null) continue; var eventType = _subsManager.GetEventTypeByName(eventName); - var integrationEvent = JsonSerializer.Deserialize(message, eventType, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); + var integrationEvent = JsonSerializer.Deserialize(message, eventType, s_caseInsensitiveOptions); var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType); await Task.Yield(); diff --git a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs index 7da4b88ee..70e778e50 100644 --- a/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs +++ b/src/BuildingBlocks/EventBus/IntegrationEventLogEF/IntegrationEventLogEntry.cs @@ -2,16 +2,16 @@ public class IntegrationEventLogEntry { + private static readonly JsonSerializerOptions s_indentedOptions = new() { WriteIndented = true }; + private static readonly JsonSerializerOptions s_caseInsensitiveOptions = new() { PropertyNameCaseInsensitive = true }; + private IntegrationEventLogEntry() { } public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId) { EventId = @event.Id; CreationTime = @event.CreationDate; EventTypeName = @event.GetType().FullName; - Content = JsonSerializer.Serialize(@event, @event.GetType(), new JsonSerializerOptions - { - WriteIndented = true - }); + Content = JsonSerializer.Serialize(@event, @event.GetType(), s_indentedOptions); State = EventStateEnum.NotPublished; TimesSent = 0; TransactionId = transactionId.ToString(); @@ -30,7 +30,7 @@ public class IntegrationEventLogEntry public IntegrationEventLogEntry DeserializeJsonContent(Type type) { - IntegrationEvent = JsonSerializer.Deserialize(Content, type, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) as IntegrationEvent; + IntegrationEvent = JsonSerializer.Deserialize(Content, type, s_caseInsensitiveOptions) as IntegrationEvent; return this; } } diff --git a/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs b/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs index bca10ca33..bc503d607 100644 --- a/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs @@ -35,15 +35,12 @@ public class RedisBasketRepository : IBasketRepository return null; } - return JsonSerializer.Deserialize(data, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + return JsonSerializer.Deserialize(data, JsonHelper.CaseInsensitiveOptions); } public async Task UpdateBasketAsync(CustomerBasket basket) { - var created = await _database.StringSetAsync(basket.BuyerId, JsonSerializer.Serialize(basket)); + var created = await _database.StringSetAsync(basket.BuyerId, JsonSerializer.Serialize(basket, JsonHelper.CaseInsensitiveOptions)); if (!created) { @@ -51,7 +48,7 @@ public class RedisBasketRepository : IBasketRepository return null; } - _logger.LogInformation("Basket item persisted succesfully."); + _logger.LogInformation("Basket item persisted successfully."); return await GetBasketAsync(basket.BuyerId); } diff --git a/src/Services/Services.Common/JsonHelper.cs b/src/Services/Services.Common/JsonHelper.cs new file mode 100644 index 000000000..2bb14c3ab --- /dev/null +++ b/src/Services/Services.Common/JsonHelper.cs @@ -0,0 +1,12 @@ +using System.Text.Json; + +namespace Services.Common +{ + public static class JsonHelper + { + public static readonly JsonSerializerOptions CaseInsensitiveOptions = new() + { + PropertyNameCaseInsensitive = true + }; + } +} diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index 952cdb187..e26f1399d 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -29,10 +29,7 @@ public class BasketService : IBasketService var responseString = await response.Content.ReadAsStringAsync(); return string.IsNullOrEmpty(responseString) ? new Basket() { BuyerId = user.Id } : - JsonSerializer.Deserialize(responseString, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); } public async Task UpdateBasket(Basket basket) @@ -82,10 +79,7 @@ public class BasketService : IBasketService var jsonResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(jsonResponse, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + return JsonSerializer.Deserialize(jsonResponse, JsonHelper.CaseInsensitiveOptions); } public async Task GetOrderDraft(string basketId) @@ -94,10 +88,7 @@ public class BasketService : IBasketService var responseString = await _apiClient.GetStringAsync(uri); - var response = JsonSerializer.Deserialize(responseString, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + var response = JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); return response; } diff --git a/src/Web/WebMVC/Services/CatalogService.cs b/src/Web/WebMVC/Services/CatalogService.cs index 9b205c6e4..0e7042934 100644 --- a/src/Web/WebMVC/Services/CatalogService.cs +++ b/src/Web/WebMVC/Services/CatalogService.cs @@ -23,10 +23,7 @@ public class CatalogService : ICatalogService var responseString = await _httpClient.GetStringAsync(uri); - var catalog = JsonSerializer.Deserialize(responseString, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + var catalog = JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); return catalog; } diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index 42129390d..b5089efe3 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -23,10 +23,7 @@ public class OrderingService : IOrderingService var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonSerializer.Deserialize(responseString, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + var response = JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); return response; } @@ -37,10 +34,7 @@ public class OrderingService : IOrderingService var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonSerializer.Deserialize>(responseString, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + var response = JsonSerializer.Deserialize>(responseString, JsonHelper.CaseInsensitiveOptions); return response; } diff --git a/src/Web/WebhookClient/Services/WebhooksClient.cs b/src/Web/WebhookClient/Services/WebhooksClient.cs index 55b0d9ab8..efdbe71e2 100644 --- a/src/Web/WebhookClient/Services/WebhooksClient.cs +++ b/src/Web/WebhookClient/Services/WebhooksClient.cs @@ -14,10 +14,7 @@ public class WebhooksClient : IWebhooksClient var client = _httpClientFactory.CreateClient("GrantClient"); var response = await client.GetAsync(_options.WebhooksUrl + "/api/v1/webhooks"); var json = await response.Content.ReadAsStringAsync(); - var subscriptions = JsonSerializer.Deserialize>(json, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + var subscriptions = JsonSerializer.Deserialize>(json, JsonHelper.CaseInsensitiveOptions); return subscriptions; } } From 42e161d1f911b7f3b78f9b4a1b6350d7cda2ac2f Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 13 Jun 2023 11:33:29 -0500 Subject: [PATCH 2/3] Use file scoped namespace. --- src/Services/Services.Common/JsonHelper.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Services/Services.Common/JsonHelper.cs b/src/Services/Services.Common/JsonHelper.cs index 2bb14c3ab..e62dc42dd 100644 --- a/src/Services/Services.Common/JsonHelper.cs +++ b/src/Services/Services.Common/JsonHelper.cs @@ -1,12 +1,11 @@ using System.Text.Json; -namespace Services.Common +namespace Services.Common; + +public static class JsonHelper { - public static class JsonHelper + public static readonly JsonSerializerOptions CaseInsensitiveOptions = new() { - public static readonly JsonSerializerOptions CaseInsensitiveOptions = new() - { - PropertyNameCaseInsensitive = true - }; - } + PropertyNameCaseInsensitive = true + }; } From 5d13c097d39a7cdbc7fca8f7516e4e437b6a83b2 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 13 Jun 2023 14:41:55 -0500 Subject: [PATCH 3/3] Address PR feedback --- .../aggregator/Services/OrderApiClient.cs | 2 +- .../Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs | 2 +- .../Basket/Basket.API/Repositories/RedisBasketRepository.cs | 4 ++-- .../Services.Common/{JsonHelper.cs => JsonDefaults.cs} | 2 +- src/Web/WebMVC/Services/BasketService.cs | 6 +++--- src/Web/WebMVC/Services/CatalogService.cs | 2 +- src/Web/WebMVC/Services/OrderingService.cs | 4 ++-- src/Web/WebhookClient/Services/WebhooksClient.cs | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) rename src/Services/Services.Common/{JsonHelper.cs => JsonDefaults.cs} (85%) diff --git a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs index 19f47e4b6..34d00da5a 100644 --- a/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Mobile.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -23,6 +23,6 @@ public class OrderApiClient : IOrderApiClient var ordersDraftResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(ordersDraftResponse, JsonHelper.CaseInsensitiveOptions); + return JsonSerializer.Deserialize(ordersDraftResponse, JsonDefaults.CaseInsensitiveOptions); } } diff --git a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs index 4ff678b63..9a237e673 100644 --- a/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs +++ b/src/ApiGateways/Web.Bff.Shopping/aggregator/Services/OrderApiClient.cs @@ -23,6 +23,6 @@ public class OrderApiClient : IOrderApiClient var ordersDraftResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(ordersDraftResponse, JsonHelper.CaseInsensitiveOptions); + return JsonSerializer.Deserialize(ordersDraftResponse, JsonDefaults.CaseInsensitiveOptions); } } diff --git a/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs b/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs index bc503d607..c8a6e3ee3 100644 --- a/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs @@ -35,12 +35,12 @@ public class RedisBasketRepository : IBasketRepository return null; } - return JsonSerializer.Deserialize(data, JsonHelper.CaseInsensitiveOptions); + return JsonSerializer.Deserialize(data, JsonDefaults.CaseInsensitiveOptions); } public async Task UpdateBasketAsync(CustomerBasket basket) { - var created = await _database.StringSetAsync(basket.BuyerId, JsonSerializer.Serialize(basket, JsonHelper.CaseInsensitiveOptions)); + var created = await _database.StringSetAsync(basket.BuyerId, JsonSerializer.Serialize(basket, JsonDefaults.CaseInsensitiveOptions)); if (!created) { diff --git a/src/Services/Services.Common/JsonHelper.cs b/src/Services/Services.Common/JsonDefaults.cs similarity index 85% rename from src/Services/Services.Common/JsonHelper.cs rename to src/Services/Services.Common/JsonDefaults.cs index e62dc42dd..5798a4a86 100644 --- a/src/Services/Services.Common/JsonHelper.cs +++ b/src/Services/Services.Common/JsonDefaults.cs @@ -2,7 +2,7 @@ namespace Services.Common; -public static class JsonHelper +public static class JsonDefaults { public static readonly JsonSerializerOptions CaseInsensitiveOptions = new() { diff --git a/src/Web/WebMVC/Services/BasketService.cs b/src/Web/WebMVC/Services/BasketService.cs index e26f1399d..0cfcb408f 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -29,7 +29,7 @@ public class BasketService : IBasketService var responseString = await response.Content.ReadAsStringAsync(); return string.IsNullOrEmpty(responseString) ? new Basket() { BuyerId = user.Id } : - JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); + JsonSerializer.Deserialize(responseString, JsonDefaults.CaseInsensitiveOptions); } public async Task UpdateBasket(Basket basket) @@ -79,7 +79,7 @@ public class BasketService : IBasketService var jsonResponse = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(jsonResponse, JsonHelper.CaseInsensitiveOptions); + return JsonSerializer.Deserialize(jsonResponse, JsonDefaults.CaseInsensitiveOptions); } public async Task GetOrderDraft(string basketId) @@ -88,7 +88,7 @@ public class BasketService : IBasketService var responseString = await _apiClient.GetStringAsync(uri); - var response = JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); + var response = JsonSerializer.Deserialize(responseString, JsonDefaults.CaseInsensitiveOptions); return response; } diff --git a/src/Web/WebMVC/Services/CatalogService.cs b/src/Web/WebMVC/Services/CatalogService.cs index 0e7042934..41e375424 100644 --- a/src/Web/WebMVC/Services/CatalogService.cs +++ b/src/Web/WebMVC/Services/CatalogService.cs @@ -23,7 +23,7 @@ public class CatalogService : ICatalogService var responseString = await _httpClient.GetStringAsync(uri); - var catalog = JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); + var catalog = JsonSerializer.Deserialize(responseString, JsonDefaults.CaseInsensitiveOptions); return catalog; } diff --git a/src/Web/WebMVC/Services/OrderingService.cs b/src/Web/WebMVC/Services/OrderingService.cs index b5089efe3..fee7bbee6 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -23,7 +23,7 @@ public class OrderingService : IOrderingService var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonSerializer.Deserialize(responseString, JsonHelper.CaseInsensitiveOptions); + var response = JsonSerializer.Deserialize(responseString, JsonDefaults.CaseInsensitiveOptions); return response; } @@ -34,7 +34,7 @@ public class OrderingService : IOrderingService var responseString = await _httpClient.GetStringAsync(uri); - var response = JsonSerializer.Deserialize>(responseString, JsonHelper.CaseInsensitiveOptions); + var response = JsonSerializer.Deserialize>(responseString, JsonDefaults.CaseInsensitiveOptions); return response; } diff --git a/src/Web/WebhookClient/Services/WebhooksClient.cs b/src/Web/WebhookClient/Services/WebhooksClient.cs index efdbe71e2..c6ff2c818 100644 --- a/src/Web/WebhookClient/Services/WebhooksClient.cs +++ b/src/Web/WebhookClient/Services/WebhooksClient.cs @@ -14,7 +14,7 @@ public class WebhooksClient : IWebhooksClient var client = _httpClientFactory.CreateClient("GrantClient"); var response = await client.GetAsync(_options.WebhooksUrl + "/api/v1/webhooks"); var json = await response.Content.ReadAsStringAsync(); - var subscriptions = JsonSerializer.Deserialize>(json, JsonHelper.CaseInsensitiveOptions); + var subscriptions = JsonSerializer.Deserialize>(json, JsonDefaults.CaseInsensitiveOptions); return subscriptions; } }