Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 268 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/AzureStorageNetMigrationV12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
# Guide for migrating to Azure.Storage.Blobs from Microsoft.Azure.Storage.Blob

This guide intends to assist customers in migrating from version 11 of the Azure Storage .NET library for Blobs to version 12.
It will focus on side-by-side comparisons for similar operations between the v12 package, [`Azure.Storage.Blobs`](https://www.nuget.org/packages/Azure.Storage.Blobs) and v11 package, [`Microsoft.Azure.Storage.Blob`](https://www.nuget.org/packages/Microsoft.Azure.Storage.Blob/).

Familiarity with the v11 client library is assumed. For those new to the Azure Storage Blobs client library for .NET, please refer to the [Quickstart](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-dotnet) for the v12 library rather than this guide.

## Table of contents

- [Migration benefits](#migration-benefits)
- [General changes](#general-changes)
- [Authentication](#authentication)
- [Package and namespaces](#package-and-namespaces)
- [Client hierarchy](#client-hierarchy)
- [Client constructors](#client-constructors)
- [Migration samples](#migration-samples)
- [Creating a Container](#creating-a-container)
- [Uploading Blobs to a Container](#uploading-blobs-to-a-container)
- [Downloading Blobs from a Container](#downloading-blobs-from-a-container)
- [Listing Blobs in a Container](#listing-blobs-in-a-container)
- [Other](#other)
- [Additional samples](#additional-samples)

## Migration benefits

To understand why we created our version 12 client libraries, you may refer to the Tech Community blog post, [Announcing the Azure Storage v12 Client Libraries](https://techcommunity.microsoft.com/t5/azure-storage/announcing-the-azure-storage-v12-client-libraries/ba-p/1482394).

In summary, the v12 client libraries for Azure Storage were created in order to address a number of areas of feedback. One of the most important being that Azure services have not been consistent in organization, naming, and API structure. Azure Storage has aligned with a set of common guidelines in order to focus on quick delivery of service features and to optimize the learning curve that comes with using the libraries. They are idiomatic, approachable, and consistent. The version 12 client libraries are also thread safe, offer both synchronous and asynchronous APIs, and have improved performance over previous versions. We have reached feature parity for major scenarios and have updated documentation featuring the v12 [Quickstart](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-dotnet), [Samples](https://docs.microsoft.com/en-us/azure/storage/common/storage-samples-dotnet?toc=/azure/storage/blobs/toc.json), and [Reference](https://docs.microsoft.com/en-us/dotnet/api/azure.storage.blobs?view=azure-dotnet) pages (for .NET Azure Storage Blob Library).

Note: The blog post linked above announces deprecation for previous versions of the library.

## General changes

### Package and namespaces

Package names and the namespaces root for version 12 Azure client libraries follow the pattern `Azure.[Area].[Service]` where the legacy libraries followed the pattern `Microsoft.Azure.[Area].[Service]`.

In this case, to install the legacy v11 package with Nuget:
```
dotnet add package Azure.Storage.Blobs
```

It is now the following for v12:
```
dotnet add package Microsoft.Azure.Storage.Blob
```

### Authentication

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine here for now, I'm wondering if we should have a general migration guide for auth stuff since some of it applies to the other libaries

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point, we may need one specific to auth since it's a pretty important and large topic.


#### Managed Identity

Legacy (v11)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For all the spots that are empty, maybe add a TODO or coming soon, or leaving it out for now and making an issue to add it to the migration guide?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, added "TODO" text.


v12

#### SAS

There are various SAS tokens that may be generated. Visit our documentation pages to learn how to [Create a User Delegation SAS](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-user-delegation-sas-create-dotnet), [Create a Service SAS](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-service-sas-create-dotnet), or [Create an Account SAS](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-sas-create-dotnet?toc=/azure/storage/blobs/toc.json).

Legacy (v11)
```c

```

v12
```c

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future maybe we can try to use code snippets from the samples, that way the snippets are frequently ran and checked.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that we should in the future! I didn't do that this time because:

  1. It wouldn't have been consistent bc we don't have all the samples
  2. The snippets are rather large for some and as opposed to a whole test method I wanted to focus on just the lines that mattered. Maybe not a good approach though.

// This code snippet creates a service level SAS that only allows reading
// from service level APIs
AccountSasBuilder sas = new AccountSasBuilder
{
// Allow access to blobs
Services = AccountSasServices.Blobs,

// Allow access to the service level APIs
ResourceTypes = AccountSasResourceTypes.Service,

// Access expires in 1 hour!
ExpiresOn = DateTimeOffset.UtcNow.AddHours(1)
};
// Allow read access
sas.SetPermissions(AccountSasPermissions.Read);

// Create a SharedKeyCredential that we can use to sign the SAS token
StorageSharedKeyCredential credential = new StorageSharedKeyCredential(StorageAccountName, StorageAccountKey);

// Build a SAS URI
UriBuilder sasUri = new UriBuilder(StorageAccountBlobUri);
sasUri.Query = sas.ToSasQueryParameters(credential).ToString();

// Create a client that can authenticate with the SAS URI
BlobServiceClient service = new BlobServiceClient(sasUri.Uri);

// Make a service request to verify we've successfully authenticated
await service.GetPropertiesAsync();
```

Summary:

#### Connection string

The following code assumes you have acquired your connection string (you can do so from the Access Keys tab under Settings in your Portal Storage Account blade). It is recommended to store it in an environment variable.

Parsing the connection string in v11 vs v12.....

Legacy (v11)
```c
string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");

// Check whether the connection string can be parsed.
CloudStorageAccount storageAccount;
if (CloudStorageAccount.TryParse(storageConnectionString, out storageAccount))
{
// If the connection string is valid, proceed with operations against Blob
// storage here.
}
else
{
// Otherwise, user needs to define the environment variable.
}
```

v12
```c
string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");

// Create a client that can authenticate with a connection string
BlobServiceClient service = new BlobServiceClient(connectionString);

// Make a service request to verify we've successfully authenticated
await service.GetPropertiesAsync();
```

### Shared Access Policies

TBD

### Client hierarchy

In the interest of simplifying the API surface we've made a three top level clients that can be used to interact with a majority of your resources: `BlobServiceClient`, `BlobContainerClient`, and `BlobClient`.

[//]: # (Blob Metadata, properties, and attributes...)

### Client constructors

| In v11 | In v12 |
|-------|--------|
| `CloudStorageAccount` | `BlobServiceClient` |
| `CloudBlobContainer` | `BlobContainerClient` |
| `CloudBlobClient` | `BlobClient` |
| `CloudBlockBlob` | `BlockBlobClient` |

### Creating a Container

v11
```c
// Create the CloudBlobClient that represents the Blob storage endpoint for the storage account.
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();

CloudBlobContainer cloudBlobContainer =
cloudBlobClient.GetContainerReference("yourcontainer");
await cloudBlobContainer.CreateAsync();
```

v12
```c
// Create a BlobServiceClient object which will be used to create a container client
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);

// Create a unique name for the container
string containerName = "yourcontainer";

// Create the container and return a container client object
BlobContainerClient containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);
```

Summary: In version 11, you would have to use the storage account endpoint to create the `CloudBlobClient`. Then you can get the container reference by calling the `GetContainerReference` method and create it by calling `CreateAsync()` on it. In version 12, you will use the `BlobServiceClient` object to then create `BlobContainerClient`.


### Uploading Blobs to a Container

v11
```c
// Assumes cloudBlobContainer already contains a reference to the container.
// filename is the intended blob name as a string
// localFilePath should be the path to the local file you want to upload

// Get a reference to the blob address, then upload the file to the blob.
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(filename);
await cloudBlockBlob.UploadFromFileAsync(localFilePath);
```

v12
```c
// Assumes containerClient already contains a reference to the container.
// filename is the intended blob name as a string
// localFilePath should be the path to the local file you want to upload

// Get a reference to a blob
BlobClient blobClient = containerClient.GetBlobClient(filename);

// Open the file and upload its data
using FileStream uploadFileStream = File.OpenRead(localFilePath);
await blobClient.UploadAsync(uploadFileStream, true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of using a named parameter here to clarify the purpose of the true?

Suggested change
await blobClient.UploadAsync(uploadFileStream, true);
await blobClient.UploadAsync(uploadFileStream, overwrite: true);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

uploadFileStream.Close();
```

Summary: In v11, you would get the `CloudBlockBlob` reference by calling the `GetBlockBlobReference` method on the container name. Calling `UploadFromFileAsync` would then create the blob if it doesn't exist or overwrite the existing one. In v12, you will get the reference to the `BlobClient` object by calling the `GetBlobClient` method on the container and then upload the blob using the `UploadAsync` method.


### Downloading Blobs from a Container

Legacy (v11)
```c
// Assumes you have already created a reference to the blob via blobClient
// downloadFilePath should be the path to the intended file to download the blob to
await cloudBlockBlob.DownloadToFileAsync(downloadFilePath, FileMode.Create);
```

v12
```c
// Assumes you have already created a reference to the blob via blobClient
// downloadFilePath should be the path to the intended file to download the blob to
BlobDownloadInfo download = await blobClient.DownloadAsync();

using (FileStream downloadFileStream = File.OpenWrite(downloadFilePath))
{
await download.Content.CopyToAsync(downloadFileStream);
downloadFileStream.Close();
}
```

### Listing Blobs in a Container

Legacy (v11)
```c
// List the blobs in the container.
// Assumes a reference to the container via `cloudBlobContainer`
BlobContinuationToken blobContinuationToken = null;
do
{
var results = await cloudBlobContainer.ListBlobsSegmentedAsync(null, blobContinuationToken);
// Get the value of the continuation token returned by the listing call.
blobContinuationToken = results.ContinuationToken;
foreach (IListBlobItem item in results.Results)
{
Console.WriteLine(item.Uri);
}
} while (blobContinuationToken != null); // Loop while the continuation token is not null.
```

v12
```c
// Get a reference to the container
BlobContainerClient container = new BlobContainerClient(connectionString, containerName);

// List all blobs in the container
await foreach (BlobItem blobItem in containerClient.GetBlobsAsync())
{
Console.WriteLine("\t" + blobItem.Name);
}
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have an example for .NET Framework applications that can't use await foreach?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean that don't use it? Sorry I didn't quite understand the ask

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ask is to provide a sync snippet and async snippet. But we already have samples for that. I think customers can look to our samples if they want to see how to do use async or sync versions of our APIs. Up to you if you want to provide both in the migration guide, but we can always link to our samples as well.


### Other

## Additional samples

More examples can be found at:
- [Azure Storage samples using v12 .NET Client Libraries](https://docs.microsoft.com/en-us/azure/storage/common/storage-samples-dotnet?toc=/azure/storage/blobs/toc.json)
2 changes: 1 addition & 1 deletion sdk/storage/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Azure Storage libraries for .NET

Azure Storage is a Microsoft-managed service providing cloud storage that is highly available, secure, durable, scalable, and redundant. Azure Storage includes Blobs (objects), Queues, and Files.
Azure Storage is a Microsoft-managed service providing cloud storage that is highly available, secure, durable, scalable, and redundant. Azure Storage includes Blobs (objects), Queues, and Files. To understand more about the intent behind these v12 client libraries visit our Tech Community blog post, [Announcing the Azure Storage v12 Client Libraries](https://techcommunity.microsoft.com/t5/azure-storage/announcing-the-azure-storage-v12-client-libraries/ba-p/1482394).

- [Azure.Storage.Blobs][blobs] is Microsoft's object storage solution for the cloud. Blob storage is optimized for storing massive amounts of unstructured data that does not adhere to a particular data model or definition, such as text or binary data.

Expand Down