diff --git a/docs/reference/transport.md b/docs/reference/transport.md index 8af799958ab..ddb09df6795 100644 --- a/docs/reference/transport.md +++ b/docs/reference/transport.md @@ -3,7 +3,7 @@ mapped_pages: - https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/transport.html --- -# Transport example [transport] +# Low level Transport example [low-level-transport] This page demonstrates how to use the low level transport to send requests. @@ -16,8 +16,10 @@ public class MyRequestParameters : RequestParameters init => Q("pretty", value); } } +``` -// ... +```csharp +using Elastic.Transport; var body = """ { @@ -49,3 +51,85 @@ var response = await client.Transport .ConfigureAwait(false); ``` +# `OnBeforeRequest` example [on-before-request] + +The `OnBeforeRequest` callback in `IElasticsearchClientSettings` can be used to dynamically modify requests. + +```csharp +var settings = new ElasticsearchClientSettings(new Uri("http://localhost:9200)) + .OnBeforeRequest(OnBeforeRequest); <1> + +RequestConfiguration? globalRequestConfiguration = null; +ConditionalWeakTable<RequestConfiguration, RequestConfiguration>? globalRequestConfigurations = null; + +void OnBeforeRequest(ElasticsearchClient client, Request request, EndpointPath endpointPath, ref PostData? postData, ref IRequestConfiguration? requestConfiguration) +{ + // Each time a request is made, the transport creates a new `BoundConfiguration` for every `IRequestConfiguration` + // that is not in the cache (based on reference equality). + + // To prevent frequent allocations of our mutated request configurations (and the secondary allocations for + // `BoundConfiguration`), we have to maintain a custom cache that maps every original request configuration to the + // mutated one. + + if (requestConfiguration is null) + { + globalRequestConfiguration = Interlocked.CompareExchange( + ref globalRequestConfiguration, + new RequestConfiguration + { + UserAgent = UserAgent.Create("my-custom-user-agent") + }, + null) ?? globalRequestConfiguration; + + requestConfiguration = globalRequestConfiguration; + return; + } + + if (requestConfiguration is not RequestConfiguration rc) + { + // Only `RequestConfiguration` (not all implementations of `IRequestConfiguration`) gets cached in the + // internal cache. + requestConfiguration = MutateRequestConfiguration(requestConfiguration); + return; + } + + // ReSharper disable InconsistentlySynchronizedField + + var cache = (Interlocked.CompareExchange( + ref globalRequestConfigurations, + new ConditionalWeakTable<RequestConfiguration, RequestConfiguration>(), + null + ) ?? globalRequestConfigurations); + + if (cache.TryGetValue(rc, out var mutatedRequestConfiguration)) + { + requestConfiguration = mutatedRequestConfiguration; + return; + } + + mutatedRequestConfiguration = MutateRequestConfiguration(rc); + +#if NET8_0_OR_GREATER + cache.TryAdd(rc, mutatedRequestConfiguration); +#else + lock (cache) + { + cache.Add(rc, mutatedRequestConfiguration); + } +#endif + + // ReSharper restore InconsistentlySynchronizedField + + return; + + RequestConfiguration MutateRequestConfiguration(IRequestConfiguration requestConfiguration) + { + return new RequestConfiguration(requestConfiguration) + { + UserAgent = UserAgent.Create("my-custom-user-agent") + }; + } +} +``` + +1. Register the `OnBeforeRequest` callback. diff --git a/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj b/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj index 9945a675d3f..0b6d8e66e0a 100644 --- a/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj +++ b/src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj @@ -31,7 +31,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Elastic.Transport" Version="0.8.0" /> + <PackageReference Include="Elastic.Transport" Version="0.8.1" /> <PackageReference Include="PolySharp" Version="1.15.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Client/ElasticsearchClient.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Client/ElasticsearchClient.cs index afda07d6c1a..5399abd131a 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Client/ElasticsearchClient.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Client/ElasticsearchClient.cs @@ -6,12 +6,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; -using System.Text.Json; using System.Threading.Tasks; using System.Threading; using Elastic.Transport; -using Elastic.Transport.Diagnostics; using Elastic.Clients.Elasticsearch.Requests; @@ -28,18 +25,23 @@ public partial class ElasticsearchClient private const string OpenTelemetrySchemaVersion = "https://opentelemetry.io/schemas/1.21.0"; private readonly ITransport<IElasticsearchClientSettings> _transport; - internal static ConditionalWeakTable<JsonSerializerOptions, IElasticsearchClientSettings> SettingsTable { get; } = new(); /// <summary> /// Creates a client configured to connect to http://localhost:9200. /// </summary> - public ElasticsearchClient() : this(new ElasticsearchClientSettings(new Uri("http://localhost:9200"))) { } + public ElasticsearchClient() : + this(new ElasticsearchClientSettings(new Uri("http://localhost:9200"))) + { + } /// <summary> /// Creates a client configured to connect to a node reachable at the provided <paramref name="uri" />. /// </summary> /// <param name="uri">The <see cref="Uri" /> to connect to.</param> - public ElasticsearchClient(Uri uri) : this(new ElasticsearchClientSettings(uri)) { } + public ElasticsearchClient(Uri uri) : + this(new ElasticsearchClientSettings(uri)) + { + } /// <summary> /// Creates a client configured to communicate with Elastic Cloud using the provided <paramref name="cloudId" />. @@ -51,8 +53,8 @@ public ElasticsearchClient(Uri uri) : this(new ElasticsearchClientSettings(uri)) /// </summary> /// <param name="cloudId">The Cloud ID of an Elastic Cloud deployment.</param> /// <param name="credentials">The credentials to use for the connection.</param> - public ElasticsearchClient(string cloudId, AuthorizationHeader credentials) : this( - new ElasticsearchClientSettings(cloudId, credentials)) + public ElasticsearchClient(string cloudId, AuthorizationHeader credentials) : + this(new ElasticsearchClientSettings(cloudId, credentials)) { } @@ -69,8 +71,7 @@ internal ElasticsearchClient(ITransport<IElasticsearchClientSettings> transport) { transport.ThrowIfNull(nameof(transport)); transport.Configuration.ThrowIfNull(nameof(transport.Configuration)); - transport.Configuration.RequestResponseSerializer.ThrowIfNull( - nameof(transport.Configuration.RequestResponseSerializer)); + transport.Configuration.RequestResponseSerializer.ThrowIfNull(nameof(transport.Configuration.RequestResponseSerializer)); transport.Configuration.Inferrer.ThrowIfNull(nameof(transport.Configuration.Inferrer)); _transport = transport; @@ -96,47 +97,38 @@ private enum ProductCheckStatus private partial void SetupNamespaces(); - internal TResponse DoRequest<TRequest, TResponse, TRequestParameters>(TRequest request) - where TRequest : Request<TRequestParameters> - where TResponse : TransportResponse, new() - where TRequestParameters : RequestParameters, new() => - DoRequest<TRequest, TResponse, TRequestParameters>(request, null); - internal TResponse DoRequest<TRequest, TResponse, TRequestParameters>( - TRequest request, - Action<IRequestConfiguration>? forceConfiguration) + TRequest request) where TRequest : Request<TRequestParameters> where TResponse : TransportResponse, new() where TRequestParameters : RequestParameters, new() - => DoRequestCoreAsync<TRequest, TResponse, TRequestParameters>(false, request, forceConfiguration).EnsureCompleted(); - - internal Task<TResponse> DoRequestAsync<TRequest, TResponse, TRequestParameters>( - TRequest request, - CancellationToken cancellationToken = default) - where TRequest : Request<TRequestParameters> - where TResponse : TransportResponse, new() - where TRequestParameters : RequestParameters, new() - => DoRequestAsync<TRequest, TResponse, TRequestParameters>(request, null, cancellationToken); + { + return DoRequestCoreAsync<TRequest, TResponse, TRequestParameters>(false, request).EnsureCompleted(); + } internal Task<TResponse> DoRequestAsync<TRequest, TResponse, TRequestParameters>( TRequest request, - Action<IRequestConfiguration>? forceConfiguration, CancellationToken cancellationToken = default) where TRequest : Request<TRequestParameters> where TResponse : TransportResponse, new() where TRequestParameters : RequestParameters, new() - => DoRequestCoreAsync<TRequest, TResponse, TRequestParameters>(true, request, forceConfiguration, cancellationToken).AsTask(); + { + return DoRequestCoreAsync<TRequest, TResponse, TRequestParameters>(true, request, cancellationToken).AsTask(); + } private ValueTask<TResponse> DoRequestCoreAsync<TRequest, TResponse, TRequestParameters>( bool isAsync, TRequest request, - Action<IRequestConfiguration>? forceConfiguration, CancellationToken cancellationToken = default) where TRequest : Request<TRequestParameters> where TResponse : TransportResponse, new() where TRequestParameters : RequestParameters, new() { - // The product check modifies request parameters and therefore must not be executed concurrently. + if (request is null) + { + throw new ArgumentNullException(nameof(request)); + } + // We use a lockless CAS approach to make sure that only a single product check request is executed at a time. // We do not guarantee that the product check is always performed on the first request. @@ -157,12 +149,12 @@ private ValueTask<TResponse> DoRequestCoreAsync<TRequest, TResponse, TRequestPar ValueTask<TResponse> SendRequest() { - var (endpointPath, resolvedRouteValues, postData) = PrepareRequest<TRequest, TRequestParameters>(request); - var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, resolvedRouteValues); + PrepareRequest<TRequest, TRequestParameters>(request, out var endpointPath, out var postData, out var requestConfiguration, out var routeValues); + var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, routeValues); return isAsync - ? new ValueTask<TResponse>(_transport.RequestAsync<TResponse>(endpointPath, postData, openTelemetryDataMutator, request.RequestConfiguration, cancellationToken)) - : new ValueTask<TResponse>(_transport.Request<TResponse>(endpointPath, postData, openTelemetryDataMutator, request.RequestConfiguration)); + ? new ValueTask<TResponse>(_transport.RequestAsync<TResponse>(endpointPath, postData, openTelemetryDataMutator, requestConfiguration, cancellationToken)) + : new ValueTask<TResponse>(_transport.Request<TResponse>(endpointPath, postData, openTelemetryDataMutator, requestConfiguration)); } async ValueTask<TResponse> SendRequestWithProductCheck() @@ -178,7 +170,9 @@ async ValueTask<TResponse> SendRequestWithProductCheck() // 32-bit read/write operations are atomic and due to the initial memory barrier, we can be sure that // no other thread executes the product check at the same time. Locked access is not required here. if (_productCheckStatus is (int)ProductCheckStatus.InProgress) + { _productCheckStatus = (int)ProductCheckStatus.NotChecked; + } throw; } @@ -186,26 +180,25 @@ async ValueTask<TResponse> SendRequestWithProductCheck() async ValueTask<TResponse> SendRequestWithProductCheckCore() { + PrepareRequest<TRequest, TRequestParameters>(request, out var endpointPath, out var postData, out var requestConfiguration, out var routeValues); + var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, routeValues); + // Attach product check header - // TODO: The copy constructor should accept null values - var requestConfig = (request.RequestConfiguration is null) - ? new RequestConfiguration() + var requestConfig = (requestConfiguration is null) + ? new RequestConfiguration { ResponseHeadersToParse = new HeadersList("x-elastic-product") } - : new RequestConfiguration(request.RequestConfiguration) + : new RequestConfiguration(requestConfiguration) { - ResponseHeadersToParse = (request.RequestConfiguration.ResponseHeadersToParse is { Count: > 0 }) - ? new HeadersList(request.RequestConfiguration.ResponseHeadersToParse, "x-elastic-product") + ResponseHeadersToParse = (requestConfiguration.ResponseHeadersToParse is { Count: > 0 }) + ? new HeadersList(requestConfiguration.ResponseHeadersToParse, "x-elastic-product") : new HeadersList("x-elastic-product") }; // Send request - var (endpointPath, resolvedRouteValues, postData) = PrepareRequest<TRequest, TRequestParameters>(request); - var openTelemetryDataMutator = GetOpenTelemetryDataMutator<TRequest, TRequestParameters>(request, resolvedRouteValues); - TResponse response; if (isAsync) @@ -239,7 +232,9 @@ async ValueTask<TResponse> SendRequestWithProductCheckCore() : (int)ProductCheckStatus.Failed; if (_productCheckStatus == (int)ProductCheckStatus.Failed) + { throw new UnsupportedProductException(UnsupportedProductException.InvalidProductError); + } return response; } @@ -249,15 +244,17 @@ async ValueTask<TResponse> SendRequestWithProductCheckCore() where TRequest : Request<TRequestParameters> where TRequestParameters : RequestParameters, new() { - // If there are no subscribed listeners, we avoid some work and allocations + // If there are no subscribed listeners, we avoid some work and allocations. if (!Elastic.Transport.Diagnostics.OpenTelemetry.ElasticTransportActivitySourceHasListeners) + { return null; + } return OpenTelemetryDataMutator; void OpenTelemetryDataMutator(Activity activity) { - // We fall back to a general operation name in cases where the derived request fails to override the property + // We fall back to a general operation name in cases where the derived request fails to override the property. var operationName = !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : request.HttpMethod.GetStringValue(); // TODO: Optimisation: We should consider caching these, either for cases where resolvedRouteValues is null, or @@ -267,7 +264,7 @@ void OpenTelemetryDataMutator(Activity activity) // The latter may bloat the cache as some combinations of path parts may rarely re-occur. activity.DisplayName = operationName; - + activity.SetTag(OpenTelemetry.SemanticConventions.DbOperation, !string.IsNullOrEmpty(request.OperationName) ? request.OperationName : "unknown"); activity.SetTag($"{OpenTelemetrySpanAttributePrefix}schema_url", OpenTelemetrySchemaVersion); @@ -282,21 +279,26 @@ void OpenTelemetryDataMutator(Activity activity) } } - private (EndpointPath endpointPath, Dictionary<string, string>? resolvedRouteValues, PostData data) PrepareRequest<TRequest, TRequestParameters>(TRequest request) + private void PrepareRequest<TRequest, TRequestParameters>( + TRequest request, + out EndpointPath endpointPath, + out PostData? postData, + out IRequestConfiguration? requestConfiguration, + out Dictionary<string, string>? routeValues) where TRequest : Request<TRequestParameters> where TRequestParameters : RequestParameters, new() { - request.ThrowIfNull(nameof(request), "A request is required."); - - var (resolvedUrl, _, routeValues) = request.GetUrl(ElasticsearchClientSettings); + var (resolvedUrl, _, resolvedRouteValues) = request.GetUrl(ElasticsearchClientSettings); var pathAndQuery = request.RequestParameters.CreatePathWithQueryStrings(resolvedUrl, ElasticsearchClientSettings); - var postData = - request.HttpMethod == HttpMethod.GET || - request.HttpMethod == HttpMethod.HEAD || !request.SupportsBody + routeValues = resolvedRouteValues; + endpointPath = new EndpointPath(request.HttpMethod, pathAndQuery); + postData = + request.HttpMethod is HttpMethod.GET or HttpMethod.HEAD || !request.SupportsBody ? null : PostData.Serializable(request); - return (new EndpointPath(request.HttpMethod, pathAndQuery), routeValues, postData); + requestConfiguration = request.RequestConfiguration; + ElasticsearchClientSettings.OnBeforeRequest?.Invoke(this, request, endpointPath, ref postData, ref requestConfiguration); } } diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Client/NamespacedClientProxy.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Client/NamespacedClientProxy.cs index 65b32e91a8a..b7aeacb4fcc 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Client/NamespacedClientProxy.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Client/NamespacedClientProxy.cs @@ -5,15 +5,16 @@ using System; using System.Threading; using System.Threading.Tasks; + using Elastic.Clients.Elasticsearch.Requests; using Elastic.Transport; -using Elastic.Transport.Products.Elasticsearch; namespace Elastic.Clients.Elasticsearch; public abstract class NamespacedClientProxy { - private const string InvalidOperation = "The client has not been initialised for proper usage as may have been partially mocked. Ensure you are using a " + + private const string InvalidOperation = + "The client has not been initialised for proper usage as may have been partially mocked. Ensure you are using a " + "new instance of ElasticsearchClient to perform requests over a network to Elasticsearch."; protected ElasticsearchClient Client { get; } @@ -21,27 +22,24 @@ public abstract class NamespacedClientProxy /// <summary> /// Initializes a new instance for mocking. /// </summary> - protected NamespacedClientProxy() { } + protected NamespacedClientProxy() + { + } internal NamespacedClientProxy(ElasticsearchClient client) => Client = client; - internal TResponse DoRequest<TRequest, TResponse, TRequestParameters>(TRequest request) - where TRequest : Request<TRequestParameters> - where TResponse : TransportResponse, new() - where TRequestParameters : RequestParameters, new() - => DoRequest<TRequest, TResponse, TRequestParameters>(request, null); - internal TResponse DoRequest<TRequest, TResponse, TRequestParameters>( - TRequest request, - Action<IRequestConfiguration>? forceConfiguration) + TRequest request) where TRequest : Request<TRequestParameters> where TResponse : TransportResponse, new() where TRequestParameters : RequestParameters, new() { if (Client is null) - ThrowHelper.ThrowInvalidOperationException(InvalidOperation); + { + throw new InvalidOperationException(InvalidOperation); + } - return Client.DoRequest<TRequest, TResponse, TRequestParameters>(request, forceConfiguration); + return Client.DoRequest<TRequest, TResponse, TRequestParameters>(request); } internal Task<TResponse> DoRequestAsync<TRequest, TResponse, TRequestParameters>( @@ -49,20 +47,13 @@ internal Task<TResponse> DoRequestAsync<TRequest, TResponse, TRequestParameters> CancellationToken cancellationToken = default) where TRequest : Request<TRequestParameters> where TResponse : TransportResponse, new() - where TRequestParameters : RequestParameters, new() - => DoRequestAsync<TRequest, TResponse, TRequestParameters>(request, null, cancellationToken); - - internal Task<TResponse> DoRequestAsync<TRequest, TResponse, TRequestParameters>( - TRequest request, - Action<IRequestConfiguration>? forceConfiguration, - CancellationToken cancellationToken = default) - where TRequest : Request<TRequestParameters> - where TResponse : TransportResponse, new() where TRequestParameters : RequestParameters, new() { if (Client is null) - ThrowHelper.ThrowInvalidOperationException(InvalidOperation); + { + throw new InvalidOperationException(InvalidOperation); + } - return Client.DoRequestAsync<TRequest, TResponse, TRequestParameters>(request, forceConfiguration, cancellationToken); + return Client.DoRequestAsync<TRequest, TResponse, TRequestParameters>(request, cancellationToken); } } diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs index 4c655162fd6..9b5197a4b95 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/ElasticsearchClientSettings.cs @@ -8,9 +8,10 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.InteropServices; using Elastic.Clients.Elasticsearch.Esql; - +using Elastic.Clients.Elasticsearch.Requests; using Elastic.Clients.Elasticsearch.Serialization; using Elastic.Transport; @@ -110,6 +111,7 @@ public abstract class ElasticsearchClientSettingsBase<TConnectionSettings> : private readonly FluentDictionary<MemberInfo, PropertyMapping> _propertyMappings = new(); private readonly FluentDictionary<Type, string> _routeProperties = new(); private readonly Serializer _sourceSerializer; + private BeforeRequestEvent? _onBeforeRequest; private bool _experimentalEnableSerializeNullInferredValues; private ExperimentalSettings _experimentalSettings = new(); @@ -158,7 +160,7 @@ protected ElasticsearchClientSettingsBase( FluentDictionary<Type, string> IElasticsearchClientSettings.RouteProperties => _routeProperties; Serializer IElasticsearchClientSettings.SourceSerializer => _sourceSerializer; - + BeforeRequestEvent? IElasticsearchClientSettings.OnBeforeRequest => _onBeforeRequest; ExperimentalSettings IElasticsearchClientSettings.Experimental => _experimentalSettings; bool IElasticsearchClientSettings.ExperimentalEnableSerializeNullInferredValues => _experimentalEnableSerializeNullInferredValues; @@ -322,6 +324,20 @@ public TConnectionSettings DefaultMappingFor(IEnumerable<ClrTypeMapping> typeMap return (TConnectionSettings)this; } + + /// <inheritdoc cref="IElasticsearchClientSettings.OnBeforeRequest"/> + public TConnectionSettings OnBeforeRequest(BeforeRequestEvent handler) + { + return Assign(handler, static (a, v) => a._onBeforeRequest += v ?? DefaultBeforeRequestHandler); + } + + private static void DefaultBeforeRequestHandler(ElasticsearchClient client, + Request request, + EndpointPath endpointPath, + ref PostData? postData, + ref IRequestConfiguration? requestConfiguration) + { + } } /// <inheritdoc cref="TransportClientConfigurationValues" /> diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/IElasticsearchClientSettings.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/IElasticsearchClientSettings.cs index 37bf49198a6..ffda6461b83 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/IElasticsearchClientSettings.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Configuration/IElasticsearchClientSettings.cs @@ -5,10 +5,26 @@ using System; using System.Collections.Generic; using System.Reflection; +using Elastic.Clients.Elasticsearch.Requests; using Elastic.Transport; namespace Elastic.Clients.Elasticsearch; +/// <summary> +/// An event that is fired before a request is sent. +/// </summary> +/// <param name="name">The <see cref="ElasticsearchClient"/> instance used to send the request.</param> +/// <param name="request">The request.</param> +/// <param name="endpointPath">The endpoint path.</param> +/// <param name="postData">The post data.</param> +/// <param name="requestConfiguration">The request configuration.</param> +public delegate void BeforeRequestEvent( + ElasticsearchClient client, + Request request, + EndpointPath endpointPath, + ref PostData? postData, + ref IRequestConfiguration? requestConfiguration); + /// <summary> /// Provides the connection settings for Elastic.Clients.Elasticsearch's high level <see cref="ElasticsearchClient" /> /// </summary> @@ -91,6 +107,14 @@ public interface IElasticsearchClientSettings : ITransportConfiguration /// </summary> Serializer SourceSerializer { get; } + /// <summary> + /// A callback that is invoked immediately before a request is sent. + /// <para> + /// Allows to dynamically update the <see cref="PostData"/> and <see cref="IRequestConfiguration"/>. + /// </para> + /// </summary> + BeforeRequestEvent? OnBeforeRequest { get; } + /// <summary> /// This is an advanced setting which controls serialization behaviour for inferred properies such as ID, routing and index name. /// <para>When enabled, it may reduce allocations on serialisation paths where the cost can be more significant, such as in bulk operations.</para> diff --git a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Request/Request.cs b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Request/Request.cs index f748358cf9d..cb269041d2f 100644 --- a/src/Elastic.Clients.Elasticsearch/_Shared/Core/Request/Request.cs +++ b/src/Elastic.Clients.Elasticsearch/_Shared/Core/Request/Request.cs @@ -57,7 +57,8 @@ protected virtual (string ResolvedUrl, string UrlTemplate, Dictionary<string, st ApiUrls.Resolve(routeValues, settings); internal virtual void BeforeRequest() - { } + { + } internal (string ResolvedUrl, string UrlTemplate, Dictionary<string, string>? resolvedRouteValues) GetUrl(IElasticsearchClientSettings settings) => ResolveUrl(RouteValues, settings); diff --git a/src/Playground/Playground.csproj b/src/Playground/Playground.csproj index 7028e3859b3..61254173cb2 100644 --- a/src/Playground/Playground.csproj +++ b/src/Playground/Playground.csproj @@ -13,7 +13,7 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Elastic.Transport" Version="0.8.0" /> + <PackageReference Include="Elastic.Transport" Version="0.8.1" /> <PackageReference Include="System.Text.Json" Version="8.0.5" /> </ItemGroup>