From fd76f390ef21da007ec018f09004f35c8c1752f7 Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 13 Jun 2023 11:31:11 -0500 Subject: [PATCH] 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 6a8465df63..19f47e4b6e 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 async Task GetOrderDraftFromBasketAsync(BasketData basket) 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 53fb65bb86..4ff678b63e 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 async Task GetOrderDraftFromBasketAsync(BasketData basket) 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 aa4dc17ef3..d27f775e8d 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 void Publish(IntegrationEvent @event) 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 @@ private async Task ProcessEvent(string eventName, string message) 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 7da4b88eec..70e778e500 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 IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId) 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 bca10ca332..bc503d6079 100644 --- a/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs +++ b/src/Services/Basket/Basket.API/Repositories/RedisBasketRepository.cs @@ -35,15 +35,12 @@ public async Task GetBasketAsync(string customerId) 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 async Task UpdateBasketAsync(CustomerBasket basket) 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 0000000000..2bb14c3ab6 --- /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 952cdb187c..e26f1399de 100644 --- a/src/Web/WebMVC/Services/BasketService.cs +++ b/src/Web/WebMVC/Services/BasketService.cs @@ -29,10 +29,7 @@ public async Task GetBasket(ApplicationUser user) 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 async Task SetQuantities(ApplicationUser user, Dictionary(jsonResponse, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + return JsonSerializer.Deserialize(jsonResponse, JsonHelper.CaseInsensitiveOptions); } public async Task GetOrderDraft(string basketId) @@ -94,10 +88,7 @@ public async Task GetOrderDraft(string basketId) 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 9b205c6e49..0e7042934b 100644 --- a/src/Web/WebMVC/Services/CatalogService.cs +++ b/src/Web/WebMVC/Services/CatalogService.cs @@ -23,10 +23,7 @@ public async Task GetCatalogItems(int page, int take, int? brand, int? 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 42129390d0..b5089efe30 100644 --- a/src/Web/WebMVC/Services/OrderingService.cs +++ b/src/Web/WebMVC/Services/OrderingService.cs @@ -23,10 +23,7 @@ async public Task GetOrder(ApplicationUser user, string id) 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 @@ async public Task> GetMyOrders(ApplicationUser user) 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 55b0d9ab85..efdbe71e2e 100644 --- a/src/Web/WebhookClient/Services/WebhooksClient.cs +++ b/src/Web/WebhookClient/Services/WebhooksClient.cs @@ -14,10 +14,7 @@ public async Task> LoadWebhooks() 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; } }