< Summary

Class:Azure.Storage.Blobs.Specialized.SpecializedBlobExtensions
Assembly:Azure.Storage.Blobs.Batch
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs.Batch\src\BlobBatchClient.cs
Covered lines:1
Uncovered lines:0
Coverable lines:1
Total lines:746
Line coverage:100% (1 of 1)
Covered branches:0
Total branches:0

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
GetBlobBatchClient(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs.Batch\src\BlobBatchClient.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.IO;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Azure.Core;
 10using Azure.Core.Pipeline;
 11using Azure.Storage.Blobs.Models;
 12
 13#pragma warning disable SA1402  // File may only contain a single type
 14
 15namespace Azure.Storage.Blobs.Specialized
 16{
 17    /// <summary>
 18    /// The <see cref="BlobBatchClient"/> allows you to batch multiple Azure
 19    /// Storage operations in a single request.
 20    /// </summary>
 21    public class BlobBatchClient
 22    {
 23        /// <summary>
 24        /// Gets the blob service's primary <see cref="Uri"/> endpoint.
 25        /// </summary>
 26        public virtual Uri Uri { get; }
 27
 28        /// <summary>
 29        /// The <see cref="HttpPipeline"/> transport pipeline used to send
 30        /// every request.
 31        /// </summary>
 32        internal virtual HttpPipeline Pipeline { get; }
 33
 34        /// <summary>
 35        /// The version of the service to use when sending requests.
 36        /// </summary>
 37        internal virtual BlobClientOptions.ServiceVersion Version { get; }
 38
 39        /// <summary>
 40        /// The <see cref="ClientDiagnostics"/> instance used to create diagnostic scopes
 41        /// every request.
 42        /// </summary>
 43        internal virtual ClientDiagnostics ClientDiagnostics { get; }
 44
 45        /// <summary>
 46        /// The <see cref="HttpPipeline"/> transport pipeline used to prepare
 47        /// requests for batching without actually sending them.
 48        /// </summary>
 49        internal virtual HttpPipeline BatchOperationPipeline { get; }
 50
 51        #region ctors
 52        /// <summary>
 53        /// Initializes a new instance of the <see cref="BlobBatchClient"/>
 54        /// class for mocking.
 55        /// </summary>
 56        protected BlobBatchClient()
 57        {
 58        }
 59
 60        /// <summary>
 61        /// Initializes a new instance of the <see cref="BlobBatchClient"/>
 62        /// class for the same account as the <see cref="BlobServiceClient"/>.
 63        /// The new <see cref="BlobBatchClient"/> uses the same request policy
 64        /// pipeline as the <see cref="BlobServiceClient"/>.
 65        /// </summary>
 66        /// <param name="client">The <see cref="BlobServiceClient"/>.</param>
 67        public BlobBatchClient(BlobServiceClient client)
 68        {
 69            Uri = client.Uri;
 70            Pipeline = BlobServiceClientInternals.GetHttpPipeline(client);
 71            BlobClientOptions options = BlobServiceClientInternals.GetClientOptions(client);
 72            Version = options.Version;
 73            ClientDiagnostics = new ClientDiagnostics(options);
 74
 75            // Construct a dummy pipeline for processing batch sub-operations
 76            // if we don't have one cached on the service
 77            BatchOperationPipeline = CreateBatchPipeline(
 78                Pipeline,
 79                BlobServiceClientInternals.GetAuthenticationPolicy(client),
 80                Version);
 81        }
 82
 83        /// <summary>
 84        /// Creates a pipeline to use for processing sub-operations before they
 85        /// are combined into a single multipart request.
 86        /// </summary>
 87        /// <param name="pipeline">
 88        /// The pipeline used to submit the live request.
 89        /// </param>
 90        /// <param name="authenticationPolicy">
 91        /// An optional <see cref="HttpPipelinePolicy"/> used to authenticate
 92        /// the sub-operations.
 93        /// </param>
 94        /// <param name="serviceVersion">
 95        /// The serviceVersion used when generating sub-requests.
 96        /// </param>
 97        /// <returns>A pipeline to use for processing sub-operations.</returns>
 98        private static HttpPipeline CreateBatchPipeline(
 99            HttpPipeline pipeline,
 100            HttpPipelinePolicy authenticationPolicy,
 101            BlobClientOptions.ServiceVersion serviceVersion)
 102        {
 103            // Configure the options to use minimal policies
 104            var options = new BlobClientOptions(serviceVersion);
 105            options.Diagnostics.IsLoggingEnabled = false;
 106            options.Diagnostics.IsTelemetryEnabled = false;
 107            options.Diagnostics.IsDistributedTracingEnabled = false;
 108            options.Retry.MaxRetries = 0;
 109
 110            // Use an empty transport so requests aren't sent
 111            options.Transport = new BatchPipelineTransport(pipeline);
 112
 113            // Use the same authentication mechanism
 114            return HttpPipelineBuilder.Build(
 115                options,
 116                RemoveVersionHeaderPolicy.Shared,
 117                authenticationPolicy);
 118        }
 119
 120        /// <summary>
 121        /// Helper to access protected static members of BlobServiceClient
 122        /// that should not be exposed directly to customers.
 123        /// </summary>
 124        private class BlobServiceClientInternals : BlobServiceClient
 125        {
 126            /// <summary>
 127            /// Prevent instantiation.
 128            /// </summary>
 129            private BlobServiceClientInternals() { }
 130
 131            /// <summary>
 132            /// Get a <see cref="BlobServiceClient"/>'s <see cref="HttpPipeline"/>
 133            /// for creating child clients.
 134            /// </summary>
 135            /// <param name="client">The BlobServiceClient.</param>
 136            /// <returns>The BlobServiceClient's HttpPipeline.</returns>
 137            public static new HttpPipeline GetHttpPipeline(BlobServiceClient client) =>
 138                BlobServiceClient.GetHttpPipeline(client);
 139
 140            /// <summary>
 141            /// Get a <see cref="BlobServiceClient"/>'s authentication
 142            /// <see cref="HttpPipelinePolicy"/> for creating child clients.
 143            /// </summary>
 144            /// <param name="client">The BlobServiceClient.</param>
 145            /// <returns>The BlobServiceClient's authentication policy.</returns>
 146            public static new HttpPipelinePolicy GetAuthenticationPolicy(BlobServiceClient client) =>
 147                BlobServiceClient.GetAuthenticationPolicy(client);
 148
 149            /// <summary>
 150            /// Get a <see cref="BlobServiceClient"/>'s <see cref="BlobClientOptions"/>
 151            /// for creating child clients.
 152            /// </summary>
 153            /// <param name="client">The BlobServiceClient.</param>
 154            /// <returns>The BlobServiceClient's BlobClientOptions.</returns>
 155            public static new BlobClientOptions GetClientOptions(BlobServiceClient client) =>
 156                BlobServiceClient.GetClientOptions(client);
 157        }
 158        #endregion ctors
 159
 160        #region Create/SubmitBatch
 161        /// <summary>
 162        /// Creates a new <see cref="BlobBatch"/> to collect sub-operations
 163        /// that can be submitted together via <see cref="SubmitBatch"/>.
 164        /// </summary>
 165        /// <returns>A new <see cref="BlobBatch"/>.</returns>
 166        public virtual BlobBatch CreateBatch() => new BlobBatch(this);
 167
 168        /// <summary>
 169        /// Submit a <see cref="BlobBatch"/> of sub-operations.
 170        /// </summary>
 171        /// <param name="batch">
 172        /// A <see cref="BlobBatch"/> of sub-operations.
 173        /// </param>
 174        /// <param name="throwOnAnyFailure">
 175        /// A value indicating whether or not to throw exceptions for
 176        /// sub-operation failures.
 177        /// </param>
 178        /// <param name="cancellationToken">
 179        /// Optional <see cref="CancellationToken"/> to propagate notifications
 180        /// that the operation should be cancelled.
 181        /// </param>
 182        /// <returns>
 183        /// A <see cref="Response"/> on successfully submitting.
 184        /// </returns>
 185        /// <remarks>
 186        /// A <see cref="RequestFailedException"/> will be thrown if
 187        /// a failure to submit the batch occurs.  Individual sub-operation
 188        /// failures will only throw if <paramref name="throwOnAnyFailure"/> is
 189        /// true and be wrapped in an <see cref="AggregateException"/>.
 190        /// </remarks>
 191[ForwardsClientCalls] // TODO: Throwing exceptions fails tests
 192        public virtual Response SubmitBatch(
 193            BlobBatch batch,
 194            bool throwOnAnyFailure = false,
 195            CancellationToken cancellationToken = default) =>
 196            SubmitBatchInternal(
 197                batch,
 198                throwOnAnyFailure,
 199                false, // async
 200                cancellationToken)
 201                .EnsureCompleted();
 202
 203        /// <summary>
 204        /// Submit a <see cref="BlobBatch"/> of sub-operations.
 205        /// </summary>
 206        /// <param name="batch">
 207        /// A <see cref="BlobBatch"/> of sub-operations.
 208        /// </param>
 209        /// <param name="throwOnAnyFailure">
 210        /// A value indicating whether or not to throw exceptions for
 211        /// sub-operation failures.
 212        /// </param>
 213        /// <param name="cancellationToken">
 214        /// Optional <see cref="CancellationToken"/> to propagate notifications
 215        /// that the operation should be cancelled.
 216        /// </param>
 217        /// <returns>
 218        /// A <see cref="Response"/> on successfully submitting.
 219        /// </returns>
 220        /// <remarks>
 221        /// A <see cref="RequestFailedException"/> will be thrown if
 222        /// a failure to submit the batch occurs.  Individual sub-operation
 223        /// failures will only throw if <paramref name="throwOnAnyFailure"/> is
 224        /// true and be wrapped in an <see cref="AggregateException"/>.
 225        /// </remarks>
 226[ForwardsClientCalls] // TODO: Throwing exceptions fails tests
 227        public virtual async Task<Response> SubmitBatchAsync(
 228            BlobBatch batch,
 229            bool throwOnAnyFailure = false,
 230            CancellationToken cancellationToken = default) =>
 231            await SubmitBatchInternal(
 232                batch,
 233                throwOnAnyFailure,
 234                true, // async
 235                cancellationToken)
 236                .ConfigureAwait(false);
 237
 238        /// <summary>
 239        /// Submit a <see cref="BlobBatch"/> of sub-operations.
 240        /// </summary>
 241        /// <param name="batch">
 242        /// A <see cref="BlobBatch"/> of sub-operations.
 243        /// </param>
 244        /// <param name="throwOnAnyFailure">
 245        /// A value indicating whether or not to throw exceptions for
 246        /// sub-operation failures.
 247        /// </param>
 248        /// <param name="async">
 249        /// Whether to invoke the operation asynchronously.
 250        /// </param>
 251        /// <param name="cancellationToken">
 252        /// Optional <see cref="CancellationToken"/> to propagate notifications
 253        /// that the operation should be cancelled.
 254        /// </param>
 255        /// <returns>
 256        /// A <see cref="Response"/> on successfully submitting.
 257        /// </returns>
 258        /// <remarks>
 259        /// A <see cref="RequestFailedException"/> will be thrown if
 260        /// a failure to submit the batch occurs.  Individual sub-operation
 261        /// failures will only throw if <paramref name="throwOnAnyFailure"/> is
 262        /// true and be wrapped in an <see cref="AggregateException"/>.
 263        /// </remarks>
 264        private async Task<Response> SubmitBatchInternal(
 265            BlobBatch batch,
 266            bool throwOnAnyFailure,
 267            bool async,
 268            CancellationToken cancellationToken)
 269        {
 270            batch = batch ?? throw new ArgumentNullException(nameof(batch));
 271            if (batch.Submitted)
 272            {
 273                throw BatchErrors.CannotResubmitBatch(nameof(batch));
 274            }
 275            else if (!batch.IsAssociatedClient(this))
 276            {
 277                throw BatchErrors.BatchClientDoesNotMatch(nameof(batch));
 278            }
 279
 280            // Get the sub-operation messages to submit
 281            IList<HttpMessage> messages = batch.GetMessagesToSubmit();
 282            if (messages.Count == 0)
 283            {
 284                throw BatchErrors.CannotSubmitEmptyBatch(nameof(batch));
 285            }
 286            // TODO: Consider validating the upper limit of 256 messages
 287
 288            // Merge the sub-operations into a single multipart/mixed Stream
 289            (Stream content, string contentType) =
 290                await MergeOperationRequests(
 291                    messages,
 292                    async,
 293                    cancellationToken)
 294                    .ConfigureAwait(false);
 295
 296            // Send the batch request
 297            Response<BlobBatchResult> batchResult =
 298                await BatchRestClient.Service.SubmitBatchAsync(
 299                    ClientDiagnostics,
 300                    Pipeline,
 301                    Uri,
 302                    body: content,
 303                    contentLength: content.Length,
 304                    multipartContentType: contentType,
 305                    version: Version.ToVersionString(),
 306                    async: async,
 307                    operationName: $"{nameof(BlobBatchClient)}.{nameof(SubmitBatch)}",
 308                    cancellationToken: cancellationToken)
 309                    .ConfigureAwait(false);
 310
 311            // Split the responses apart and update the sub-operation responses
 312            Response raw = batchResult.GetRawResponse();
 313            await UpdateOperationResponses(
 314                messages,
 315                raw,
 316                batchResult.Value.Content,
 317                batchResult.Value.ContentType,
 318                throwOnAnyFailure,
 319                async,
 320                cancellationToken)
 321                .ConfigureAwait(false);
 322
 323            // Return the batch result
 324            return raw;
 325        }
 326
 327        /// <summary>
 328        /// Merge the batch sub-operation messages into a single content stream
 329        /// and content type.
 330        /// </summary>
 331        /// <param name="messages">
 332        /// The batch sub-operation messages to submit together.
 333        /// </param>
 334        /// <param name="async">
 335        /// Whether to invoke the operation asynchronously.
 336        /// </param>
 337        /// <param name="cancellationToken">
 338        /// Optional <see cref="CancellationToken"/> to propagate notifications
 339        /// that the operation should be cancelled.
 340        /// </param>
 341        /// <returns>
 342        /// A tuple containing the batch sub-operation messages merged into a
 343        /// single multipart/mixed content stream and content type.
 344        /// </returns>
 345        private async Task<(Stream, string)> MergeOperationRequests(
 346            IList<HttpMessage> messages,
 347            bool async,
 348            CancellationToken cancellationToken)
 349        {
 350            // Send all of the requests through a batch sub-operation pipeline
 351            // to prepare the requests with various headers like Authorization
 352            foreach (HttpMessage message in messages)
 353            {
 354                if (async)
 355                {
 356                    await BatchOperationPipeline.SendAsync(message, cancellationToken).ConfigureAwait(false);
 357                }
 358                else
 359                {
 360                    BatchOperationPipeline.Send(message, cancellationToken);
 361                }
 362            }
 363
 364            // Build the multipart/mixed request body
 365            return await Multipart.CreateAsync(
 366                messages,
 367                "batch",
 368                async,
 369                cancellationToken)
 370                .ConfigureAwait(false);
 371        }
 372
 373        /// <summary>
 374        /// Split the batch multipart response into individual sub-operation
 375        /// responses and update the delayed responses already returned when
 376        /// the sub-operation was added.
 377        /// </summary>
 378        /// <param name="messages">
 379        /// The batch sub-operation messages that were submitted.
 380        /// </param>
 381        /// <param name="rawResponse">
 382        /// The raw batch response.
 383        /// </param>
 384        /// <param name="responseContent">
 385        /// The raw multipart response content.
 386        /// </param>
 387        /// <param name="responseContentType">
 388        /// The raw multipart response content type (containing the boundary).
 389        /// </param>
 390        /// <param name="throwOnAnyFailure">
 391        /// A value indicating whether or not to throw exceptions for
 392        /// sub-operation failures.
 393        /// </param>
 394        /// <param name="async">
 395        /// Whether to invoke the operation asynchronously.
 396        /// </param>
 397        /// <param name="cancellationToken">
 398        /// Optional <see cref="CancellationToken"/> to propagate notifications
 399        /// that the operation should be cancelled.
 400        /// </param>
 401        /// <returns>A Task representing the update operation.</returns>
 402        private async Task UpdateOperationResponses(
 403            IList<HttpMessage> messages,
 404            Response rawResponse,
 405            Stream responseContent,
 406            string responseContentType,
 407            bool throwOnAnyFailure,
 408            bool async,
 409            CancellationToken cancellationToken)
 410        {
 411            // Parse the response content into individual responses
 412            Response[] responses;
 413            try
 414            {
 415                responses = await Multipart.ParseAsync(
 416                    responseContent,
 417                    responseContentType,
 418                    async,
 419                    cancellationToken)
 420                    .ConfigureAwait(false);
 421
 422                // Ensure we have the right number of responses
 423                if (messages.Count != responses.Length)
 424                {
 425                    // If we get one response and it's a 400, this is the
 426                    // service failing the entire batch and sending it back in
 427                    // a format not currently documented by the spec
 428                    if (responses.Length == 1 && responses[0].Status == 400)
 429                    {
 430                        // We'll re-process this response as a batch result
 431                        BatchRestClient.Service.SubmitBatchAsync_CreateResponse(ClientDiagnostics, responses[0]);
 432                    }
 433                    else
 434                    {
 435                        throw BatchErrors.UnexpectedResponseCount(messages.Count, responses.Length);
 436                    }
 437                }
 438            }
 439            catch (InvalidOperationException ex)
 440            {
 441                // Wrap any parsing errors in a RequestFailedException
 442                throw BatchErrors.InvalidResponse(ClientDiagnostics, rawResponse, ex);
 443            }
 444
 445            // Update the delayed responses
 446            List<Exception> failures = new List<Exception>();
 447            for (int i = 0; i < responses.Length; i++)
 448            {
 449                try
 450                {
 451                    if (messages[i].TryGetProperty(BatchConstants.DelayedResponsePropertyName, out object value) &&
 452                        value is DelayedResponse response)
 453                    {
 454                        response.SetLiveResponse(responses[i], throwOnAnyFailure);
 455                    }
 456                }
 457                catch (Exception ex)
 458                {
 459                    failures.Add(ex);
 460                }
 461            }
 462
 463            // Throw any failures
 464            if (failures.Count > 0)
 465            {
 466                throw BatchErrors.ResponseFailures(failures);
 467            }
 468        }
 469        #endregion Create/SubmitBatch
 470
 471        #region DeleteBlobs
 472        /// <summary>
 473        /// The DeleteBlobs operation marks the specified blobs for deletion.
 474        /// The blobs are later deleted during garbage collection.  All of the
 475        /// deletions are sent as a single batched request.
 476        /// </summary>
 477        /// <param name="cancellationToken">
 478        /// Optional <see cref="CancellationToken"/> to propagate notifications
 479        /// that the operation should be cancelled.
 480        /// </param>
 481        /// <param name="blobUris">URIs of the blobs to delete.</param>
 482        /// <param name="snapshotsOption">
 483        /// Specifies options for deleting blob snapshots.
 484        /// </param>
 485        /// <returns>
 486        /// The <see cref="Response"/>s for the individual Delete operations.
 487        /// </returns>
 488        /// <remarks>
 489        /// A <see cref="RequestFailedException"/> will be thrown if
 490        /// a failure to submit the batch occurs.  Individual sub-operation
 491        /// failures will be wrapped in an <see cref="AggregateException"/>.
 492        /// </remarks>
 493        [ForwardsClientCalls]
 494        public virtual Response[] DeleteBlobs(
 495            IEnumerable<Uri> blobUris,
 496            DeleteSnapshotsOption snapshotsOption = default,
 497            CancellationToken cancellationToken = default) =>
 498            DeleteBlobsInteral(
 499                blobUris,
 500                snapshotsOption,
 501                false, // async
 502                cancellationToken)
 503                .EnsureCompleted();
 504
 505        /// <summary>
 506        /// The DeleteBlobsAsync operation marks the specified blobs for
 507        /// deletion.  The blobs are later deleted during garbage collection.
 508        /// All of the deletions are sent as a single batched request.
 509        /// </summary>
 510        /// <param name="cancellationToken">
 511        /// Optional <see cref="CancellationToken"/> to propagate notifications
 512        /// that the operation should be cancelled.
 513        /// </param>
 514        /// <param name="blobUris">URIs of the blobs to delete.</param>
 515        /// <param name="snapshotsOption">
 516        /// Specifies options for deleting blob snapshots.
 517        /// </param>
 518        /// <returns>
 519        /// The <see cref="Response"/>s for the individual Delete operations.
 520        /// </returns>
 521        /// <remarks>
 522        /// A <see cref="RequestFailedException"/> will be thrown if
 523        /// a failure to submit the batch occurs.  Individual sub-operation
 524        /// failures will be wrapped in an <see cref="AggregateException"/>.
 525        /// </remarks>
 526        [ForwardsClientCalls]
 527        public virtual async Task<Response[]> DeleteBlobsAsync(
 528            IEnumerable<Uri> blobUris,
 529            DeleteSnapshotsOption snapshotsOption = default,
 530            CancellationToken cancellationToken = default) =>
 531            await DeleteBlobsInteral(
 532                blobUris,
 533                snapshotsOption,
 534                true, // async
 535                cancellationToken)
 536                .ConfigureAwait(false);
 537
 538        /// <summary>
 539        /// The DeleteBlobsAsync operation marks the specified blobs for
 540        /// deletion.  The blobs are later deleted during garbage collection.
 541        /// All of the deletions are sent as a single batched request.
 542        /// </summary>
 543        /// <param name="blobUris">URIs of the blobs to delete.</param>
 544        /// <param name="snapshotsOption">
 545        /// Specifies options for deleting blob snapshots.
 546        /// </param>
 547        /// <param name="async">
 548        /// Whether to invoke the operation asynchronously.
 549        /// </param>
 550        /// <param name="cancellationToken">
 551        /// Optional <see cref="CancellationToken"/> to propagate notifications
 552        /// that the operation should be cancelled.
 553        /// </param>
 554        /// <returns>
 555        /// The <see cref="Response"/>s for the individual Delete operations.
 556        /// </returns>
 557        /// <remarks>
 558        /// A <see cref="RequestFailedException"/> will be thrown if
 559        /// a failure to submit the batch occurs.  Individual sub-operation
 560        /// failures will be wrapped in an <see cref="AggregateException"/>.
 561        /// </remarks>
 562        internal async Task<Response[]> DeleteBlobsInteral(
 563            IEnumerable<Uri> blobUris,
 564            DeleteSnapshotsOption snapshotsOption,
 565            bool async,
 566            CancellationToken cancellationToken)
 567        {
 568            blobUris = blobUris ?? throw new ArgumentNullException(nameof(blobUris));
 569            var responses = new List<Response>();
 570
 571            // Create the batch
 572            BlobBatch batch = CreateBatch();
 573            foreach (Uri uri in blobUris)
 574            {
 575                responses.Add(batch.DeleteBlob(uri, snapshotsOption));
 576            }
 577
 578            // Submit the batch
 579            await SubmitBatchInternal(
 580                batch,
 581                true,
 582                async,
 583                cancellationToken)
 584                .ConfigureAwait(false);
 585
 586            return responses.ToArray();
 587        }
 588        #endregion DeleteBlobs
 589
 590        #region SetBlobsAccessTier
 591        /// <summary>
 592        /// The SetBlobsAccessTier operation sets the tier on blobs.  The
 593        /// operation is allowed on block blobs in a blob storage or general
 594        /// purpose v2 account.
 595        /// </summary>
 596        /// <param name="blobUris">URIs of the blobs to set the tiers of.</param>
 597        /// <param name="accessTier">
 598        /// Indicates the tier to be set on the blobs.
 599        /// </param>
 600        /// <param name="rehydratePriority">
 601        /// Optional <see cref="RehydratePriority"/>
 602        /// Indicates the priority with which to rehydrate an archived blob.
 603        /// </param>
 604        /// <param name="cancellationToken">
 605        /// Optional <see cref="CancellationToken"/> to propagate notifications
 606        /// that the operation should be cancelled.
 607        /// </param>
 608        /// <returns>
 609        /// The <see cref="Response"/>s for the individual Set Tier operations.
 610        /// </returns>
 611        /// <remarks>
 612        /// A <see cref="RequestFailedException"/> will be thrown if
 613        /// a failure to submit the batch occurs.  Individual sub-operation
 614        /// failures will be wrapped in an <see cref="AggregateException"/>.
 615        /// </remarks>
 616        [ForwardsClientCalls]
 617        public virtual Response[] SetBlobsAccessTier(
 618            IEnumerable<Uri> blobUris,
 619            AccessTier accessTier,
 620            RehydratePriority? rehydratePriority = default,
 621            CancellationToken cancellationToken = default) =>
 622            SetBlobsAccessTierInteral(
 623                blobUris,
 624                accessTier,
 625                rehydratePriority,
 626                false, // async
 627                cancellationToken)
 628                .EnsureCompleted();
 629
 630        /// <summary>
 631        /// The SetBlobsAccessTierAsync operation sets the tier on blobs.  The
 632        /// operation is allowed on block blobs in a blob storage or general
 633        /// purpose v2 account.
 634        /// </summary>
 635        /// <param name="blobUris">URIs of the blobs to set the tiers of.</param>
 636        /// <param name="accessTier">
 637        /// Indicates the tier to be set on the blobs.
 638        /// </param>
 639        /// <param name="rehydratePriority">
 640        /// Optional <see cref="RehydratePriority"/>
 641        /// Indicates the priority with which to rehydrate an archived blob.
 642        /// </param>
 643        /// <param name="cancellationToken">
 644        /// Optional <see cref="CancellationToken"/> to propagate notifications
 645        /// that the operation should be cancelled.
 646        /// </param>
 647        /// <returns>
 648        /// The <see cref="Response"/>s for the individual Set Tier operations.
 649        /// </returns>
 650        /// <remarks>
 651        /// A <see cref="RequestFailedException"/> will be thrown if
 652        /// a failure to submit the batch occurs.  Individual sub-operation
 653        /// failures will be wrapped in an <see cref="AggregateException"/>.
 654        /// </remarks>
 655        [ForwardsClientCalls]
 656        public virtual async Task<Response[]> SetBlobsAccessTierAsync(
 657            IEnumerable<Uri> blobUris,
 658            AccessTier accessTier,
 659            RehydratePriority? rehydratePriority = default,
 660            CancellationToken cancellationToken = default) =>
 661            await SetBlobsAccessTierInteral(
 662                blobUris,
 663                accessTier,
 664                rehydratePriority,
 665                true, // async
 666                cancellationToken)
 667                .ConfigureAwait(false);
 668
 669        /// <summary>
 670        /// The SetBlobsAccessTierAsync operation sets the tier on blobs.  The
 671        /// operation is allowed on block blobs in a blob storage or general
 672        /// purpose v2 account.
 673        /// </summary>
 674        /// <param name="blobUris">
 675        /// URIs of the blobs to set the tiers of.
 676        /// </param>
 677        /// <param name="accessTier">
 678        /// Indicates the tier to be set on the blobs.
 679        /// </param>
 680        /// <param name="rehydratePriority">
 681        /// Optional <see cref="RehydratePriority"/>
 682        /// Indicates the priority with which to rehydrate an archived blob.
 683        /// </param>
 684        /// <param name="async">
 685        /// Whether to invoke the operation asynchronously.
 686        /// </param>
 687        /// <param name="cancellationToken">
 688        /// Optional <see cref="CancellationToken"/> to propagate notifications
 689        /// that the operation should be cancelled.
 690        /// </param>
 691        /// <returns>
 692        /// The <see cref="Response"/>s for the individual Set Tier operations.
 693        /// </returns>
 694        /// <remarks>
 695        /// A <see cref="RequestFailedException"/> will be thrown if
 696        /// a failure to submit the batch occurs.  Individual sub-operation
 697        /// failures will be wrapped in an <see cref="AggregateException"/>.
 698        /// </remarks>
 699        internal async Task<Response[]> SetBlobsAccessTierInteral(
 700            IEnumerable<Uri> blobUris,
 701            AccessTier accessTier,
 702            RehydratePriority? rehydratePriority,
 703            bool async,
 704            CancellationToken cancellationToken)
 705        {
 706            blobUris = blobUris ?? throw new ArgumentNullException(nameof(blobUris));
 707            var responses = new List<Response>();
 708
 709            // Create the batch
 710            BlobBatch batch = CreateBatch();
 711            foreach (Uri uri in blobUris)
 712            {
 713                responses.Add(batch.SetBlobAccessTier(uri, accessTier, rehydratePriority));
 714            }
 715
 716            // Submit the batch
 717            await SubmitBatchInternal(
 718                batch,
 719                true,
 720                async,
 721                cancellationToken)
 722                .ConfigureAwait(false);
 723
 724            return responses.ToArray();
 725        }
 726        #endregion SetBlobsAccessTier
 727    }
 728
 729    /// <summary>
 730    /// Add easy to discover methods to <see cref="BlobServiceClient"/> for
 731    /// creating <see cref="BlobBatchClient"/> instances.
 732    /// </summary>
 733    public static partial class SpecializedBlobExtensions
 734    {
 735        /// <summary>
 736        /// Create a new <see cref="BlobBatchClient"/> object for the same
 737        /// account as the <see cref="BlobServiceClient"/>.  The new
 738        /// <see cref="BlobBatchClient"/> uses the same request policy pipeline
 739        /// as the <see cref="BlobServiceClient"/>.
 740        /// </summary>
 741        /// <param name="client">The <see cref="BlobServiceClient"/>.</param>
 742        /// <returns>A new <see cref="BlobBatchClient"/> instance.</returns>
 743        public static BlobBatchClient GetBlobBatchClient(this BlobServiceClient client)
 116744            => new BlobBatchClient(client);
 745    }
 746}

Methods/Properties

GetBlobBatchClient(...)