From d789e24e6ee72305fb8055f7b49baaca054d81b0 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 31 Aug 2020 11:56:52 -0500 Subject: [PATCH 1/3] redame content and some additional samples --- sdk/tables/Azure.Data.Tables/readme.md | 205 ++++++++++++++++-- .../Azure.Data.Tables/samples/README.md | 12 +- .../Azure.Data.Tables/samples/Sample0Auth.md | 113 ++++++++++ .../samples/Sample1CreateDeleteTables.md | 3 + .../samples/Sample2CreateDeleteEntities.md | 5 + .../samples/Sample3QueryTables.md | 4 + .../samples/Sample4QueryEntities.md | 5 +- .../tests/samples/Sample0_Auth.cs | 100 +++++++++ .../samples/Sample1_CreateDeleteTable.cs | 3 + .../samples/Sample2_CreateDeleteEntities.cs | 7 + .../tests/samples/Sample3_QueryTables.cs | 3 + .../tests/samples/Sample4_QueryEntities.cs | 5 +- .../tests/samples/Sample6_Troubleshooting.cs | 47 ++++ 13 files changed, 492 insertions(+), 20 deletions(-) create mode 100644 sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md create mode 100644 sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs create mode 100644 sdk/tables/Azure.Data.Tables/tests/samples/Sample6_Troubleshooting.cs diff --git a/sdk/tables/Azure.Data.Tables/readme.md b/sdk/tables/Azure.Data.Tables/readme.md index dca888fb2d335..d38d5b5218846 100644 --- a/sdk/tables/Azure.Data.Tables/readme.md +++ b/sdk/tables/Azure.Data.Tables/readme.md @@ -1,34 +1,207 @@ # Azure Tables client library for .NET -Content forthcoming +Azure Table storage is a service that stores large amounts of structured NoSQL data in the cloud, providing +a key/attribute store with a schema-less design. -## Getting started +Azure Cosmos DB provides a Table API for applications that are written for Azure Table storage and that need premium capabilities like: + +- Turnkey global distribution. +- Dedicated throughput worldwide. +- Single-digit millisecond latencies at the 99th percentile. +- Guaranteed high availability. +- Automatic secondary indexing. -Content forthcoming +The Azure Tables client library can seamlessly target either Azure table storage or Azure Cosmos DB table service endpoints with no code changes. + +## Getting started ### Install the package +Install the Azure Tables client library for .NET with [NuGet][nuget]: + +``` +dotnet add package Azure.Data.Tables --version 3.0.0-beta.1 +``` + +### Prerequisites +* An [Azure subscription][azure_sub]. +* An existing Azure storage account or Azure Cosmos DB database with Azure Table API specified. + +If you need to create either of these, you can use the [Azure CLI][azure_cli]. -Content forthcoming +#### Creating a storage account -### Authenticate the client +Create a storage account `mystorageaccount` in resource group `MyResourceGroup` +in the subscription `MySubscription` in the West US region. +``` +az storage account create -n mystorageaccount -g MyResourceGroup -l westus --subscription MySubscription +``` -Content forthcoming +#### Creating a Cosmos DB + +Create a storage account `MyCosmosDBDatabaseAccount` in resource group `MyResourceGroup` +in the subscription `MySubscription` and a table named `MyTableName` in the account. + +``` +az cosmosdb create --name MyCosmosDBDatabaseAccount --resource-group MyResourceGroup --subscription MySubscription + +az cosmosdb table create --name MyTableName --resource-group MyResourceGroup --account-name MyCosmosDBDatabaseAccount +``` ## Key concepts -Content forthcoming +Common uses of the Table service include: + +- Storing TBs of structured data capable of serving web scale applications +- Storing datasets that don't require complex joins, foreign keys, or stored procedures and can be de-normalized for fast access +- Quickly querying data using a clustered index +- Accessing data using the OData protocol and LINQ filter expressions + +Learn more about options for authentication _(including Connection Strings, Shared Key, and Shared Key Signatures)_ [in our samples.](samples/Sample0Auth.md) ## Examples -Content forthcoming +### Create, Get, and Delete an Azure table + +First, we need to construct a `TableServiceClient`. + +```C# Snippet:TablesSample1CreateClient +// Construct a new using a . + +var serviceClient = new TableServiceClient( + new Uri(storageUri), + new TableSharedKeyCredential(accountName, storageAccountKey)); +``` + +Next, we can create a new table. + +```C# Snippet:TablesSample1CreateTable +// Create a new table. The class stores properties of the created table. + +TableItem table = serviceClient.CreateTable(tableName); +Console.WriteLine($"The created table's name is {table.TableName}."); +``` + +The set of existing Azure tables can be queries using an OData filter. + +```C# Snippet:TablesSample3QueryTables +// Use the to query the service. Passing in OData filter strings is optional. + +Pageable queryTableResults = serviceClient.GetTables(filter: $"TableName eq '{tableName}'"); + +Console.WriteLine("The following are the names of the tables in the query results:"); + +// Iterate the in order to access queried tables. + +foreach (TableItem table in queryTableResults) +{ + Console.WriteLine(table.TableName); +} +``` + +Individual tables can bel deleted from the service. + +```C# Snippet:TablesSample1DeleteTable +// Deletes the table made previously. + +serviceClient.DeleteTable(tableName); +``` + +### Add, Query, and Delete table entities + +To interact with table entities, we must first construct a `TableClient`. + +```C# Snippet:TablesSample2CreateTableWithTableClient +// Construct a new using a . + +var tableClient = new TableClient( + new Uri(storageUri), + tableName, + new TableSharedKeyCredential(accountName, storageAccountKey)); + +// Create the table in the service. +tableClient.Create(); +``` + +Let's define a new `TableEntity` so that we can add it to the table. + +```C# Snippet:TablesSample2CreateDictionaryEntity +// Make a dictionary entity by defining a . + +var entity = new TableEntity(partitionKey, rowKey) +{ + { "Product", "Marker Set" }, + { "Price", 5.00 }, + { "Quantity", 21 } +}; + +Console.WriteLine($"{entity.RowKey}: {entity["Product"]} costs ${entity.GetDouble("Price")}."); +``` + +Using the `TableClient` we can now add our new entity to the table. + +```C# Snippet:TablesSample2AddEntity +// Add the newly created entity. + +tableClient.AddEntity(entity); +``` + +To inspect the set of existing table entities, we can query the table using an OData filter. + +```C# Snippet:TablesSample4QueryEntitiesFilter +Pageable queryResultsFilter = tableClient.Query(filter: $"PartitionKey eq '{partitionKey}'"); + +// Iterate the to access all queried entities. + +foreach (TableEntity qEntity in queryResultsFilter) +{ + Console.WriteLine($"{qEntity.GetString("Product")}: {qEntity.GetDouble("Price")}"); +} + +Console.WriteLine($"The query returned {queryResultsFilter.Count()} entities."); +``` + +If we no longer need our new table entity, it can be deleted. + +```C# Snippet:TablesSample2DeleteEntity +// Delete the entity given the partition and row key. + +tableClient.DeleteEntity(partitionKey, rowKey); +``` ## Troubleshooting -Content forthcoming +When you interact with the Azure table library using the .NET SDK, errors returned by the service correspond to the same HTTP +status codes returned for [REST API][keyvault_rest] requests. + +For example, if you try to retrieve a create a table that already exists, a `409` error is returned, indicating "Conflict". + +```C# Snippet:CreateDuplicateTable +// Construct a new TableClient using a connection string. + +var client = new TableClient( + connectionString, + tableName); + +// Create the table if it doesn't already exist. + +client.CreateIfNotExists(); + +// Now attempt to create the same table unconditionally. + +try +{ + client.Create(); +} +catch (RequestFailedException ex) when (ex.Status == (int)HttpStatusCode.Conflict) +{ + Console.WriteLine(ex.ToString()); +} +``` ## Next steps -Content forthcoming +Get started with our [Table samples](samples/README.MD): + ## Contributing @@ -38,10 +211,16 @@ the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla]. This project has adopted the [Microsoft Open Source Code of Conduct][coc]. -For more information see the [Code of Conduct FAQ][coc_faq] -or contact [opencode@microsoft.com][coc_contact] with any -additional questions or comments. +For more information see the [Code of Conduct FAQ][coc_faq] or contact +[opencode@microsoft.com][coc_contact] with any additional questions or comments. +[azure_cli]: https://docs.microsoft.com/cli/azure +[azure_sub]: https://azure.microsoft.com/free/ +[contrib]: ./CONTRIBUTING.md +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com ![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Ftables%2FAzure.Data.Tables%2FREADME.png) diff --git a/sdk/tables/Azure.Data.Tables/samples/README.md b/sdk/tables/Azure.Data.Tables/samples/README.md index 82f62a7127a5b..d48c8583848a6 100644 --- a/sdk/tables/Azure.Data.Tables/samples/README.md +++ b/sdk/tables/Azure.Data.Tables/samples/README.md @@ -14,13 +14,15 @@ description: Samples for the Azure.Data.Tables client library Description of Azure Tables. Covers following functions: * Create and delete a table * Includes error handling -* Create and delete entities * Query tables +* Create and delete entities * Query entities You can find samples for each of this main functions below. -To get started you'll need an Azure Tables endpoint and credentials. See Azure Tables Client Library [Readme][README] for more information and instructions. - -- [Create/delete tables](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/tables/Azure.Data.Tables/samples/Sample1CreateDeleteTables.md) +To get started you'll need an Azure Tables endpoint and credentials. See Azure Tables Client Library [Readme](../readme.md) for more information and instructions. -[README]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/tables/Azure.Data.Tables/readme.md +- [Create/delete tables](Sample1CreateDeleteTables.md) +- [Query tables](Sample3QueryTables.md) +- [Create/delete table entities](Sample2CreateDeleteEntities.md) +- [Query table entities](Sample4QueryEntities.md) +- [Auth](Sample0Auth.md): Authenticate with connection strings, shared keys, and shared access signatures0. diff --git a/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md b/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md new file mode 100644 index 0000000000000..a58a716e2c182 --- /dev/null +++ b/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md @@ -0,0 +1,113 @@ +# Authentication + +Every request made against an Azure table must be authorized using a connection string, +shared key credential, or shared access signature. The samples below demonstrate the usage +of these methods. + +## Connection string + +A connection string includes the authentication information required for your application to +access data in an Azure table at runtime using Shared Key authorization. + +You can obtain your connection string from the Azure Portal (click **Access Keys** under Settings +in the Portal Storage account blade or **Connection String** under Settings in the Portal Cosmos DB +account blade) or using the Azure CLI with: + +``` +az storage account show-connection-string --name --resource-group +``` +or +``` + az cosmosdb list-connection-strings --name --resource-group +``` + +```C# Snippet:TablesAuthConnString +// Construct a new TableClient using a connection string. + +var client = new TableClient( + connectionString, + tableName); + +// Create the table if it doesn't already exist to verify we've successfully authenticated. + +await client.CreateIfNotExistsAsync(); +``` + +## Shared Key Credential + +Shared Key authorization relies on your account access keys and other parameters to produce +an encrypted signature string that is passed on the request in the Authorization header. + +You'll need a Storage or Cosmos DB **account name**, **primary key**, and **endpoint** Uri. +You can obtain both from the Azure Portal by clicking **Access Keys** under Settings in the +Portal Storage account blade or **Connection String** under Settings in the Portal Cosmos DB +account blade. + +You can also get access to your account keys from the Azure CLI with: +``` +az storage account keys list --account-name --resource-group +``` +or +``` +az cosmosdb list-keys --name --resource-group +``` + +```C# Snippet:TablesAuthSharedKey +// Construct a new TableClient using a TableSharedKeyCredential. + +var client = new TableClient( + new Uri(storageUri), + tableName, + new TableSharedKeyCredential(accountName, accountKey)); + +// Create the table if it doesn't already exist to verify we've successfully authenticated. + +await client.CreateIfNotExistsAsync(); +``` + +## Shared Access Signature (SAS) + +A shared access signature allows administrators to delegate granular access to an Azure table +without sharing the access key directly. You can control what resources the client may access, +what permissions they have on those resources, and how long the SAS is valid, among other parameters. +It relies on your account access keys and other parameters to produce an encrypted signature string +that is passed on the request in the query string. + +To generate a new SAS, you must first start with a Storage or Cosmos DB **account name**, **primary key**, and **endpoint** Uri. +You can obtain both from the Azure Portal by clicking **Access Keys** under Settings in the +Portal Storage account blade or **Connection String** under Settings in the Portal Cosmos DB +account blade. + +```C# Snippet:TablesAuthSas +// Construct a new using a . + +var credential = new TableSharedKeyCredential(accountName, accountKey); + +var serviceClient = new TableServiceClient( + new Uri(storageUri), + credential); + +// Build a shared access signature with the Write and Delete permissions and access to all service resource types. + +TableAccountSasBuilder sasWriteDelete = serviceClient.GetSasBuilder(TableAccountSasPermissions.Write, TableAccountSasResourceTypes.All, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); +string tokenWriteDelete = sasWriteDelete.Sign(credential); + +// Build SAS URIs. + +UriBuilder sasUriWriteDelete = new UriBuilder(storageUri) +{ + Query = tokenWriteDelete +}; + +// Create the TableServiceClients using the SAS URIs. + +var serviceClientWithSas = new TableServiceClient(sasUriWriteDelete.Uri); + +// Validate that we are able to create a table using the SAS URI with Write and Delete permissions. + +await serviceClientWithSas.CreateTableIfNotExistsAsync(tableName); + +// Validate that we are able to delete a table using the SAS URI with Write and Delete permissions. + +await serviceClientWithSas.DeleteTableAsync(tableName); +``` diff --git a/sdk/tables/Azure.Data.Tables/samples/Sample1CreateDeleteTables.md b/sdk/tables/Azure.Data.Tables/samples/Sample1CreateDeleteTables.md index 611b6431d351c..a2b54f5c0289b 100644 --- a/sdk/tables/Azure.Data.Tables/samples/Sample1CreateDeleteTables.md +++ b/sdk/tables/Azure.Data.Tables/samples/Sample1CreateDeleteTables.md @@ -8,6 +8,7 @@ In the sample below, we will use a Storage account and authenticate with an endp ```C# Snippet:TablesSample1CreateClient // Construct a new using a . + var serviceClient = new TableServiceClient( new Uri(storageUri), new TableSharedKeyCredential(accountName, storageAccountKey)); @@ -38,6 +39,7 @@ To create a table, invoke `CreateTable` with the table name. ```C# Snippet:TablesSample1CreateTable // Create a new table. The class stores properties of the created table. + TableItem table = serviceClient.CreateTable(tableName); Console.WriteLine($"The created table's name is {table.TableName}."); ``` @@ -54,6 +56,7 @@ To delete the table, invoke `DeleteTable` with the table name. ```C# Snippet:TablesSample1DeleteTable // Deletes the table made previously. + serviceClient.DeleteTable(tableName); ``` diff --git a/sdk/tables/Azure.Data.Tables/samples/Sample2CreateDeleteEntities.md b/sdk/tables/Azure.Data.Tables/samples/Sample2CreateDeleteEntities.md index e3fc20a433ead..a40e46cba6ea1 100644 --- a/sdk/tables/Azure.Data.Tables/samples/Sample2CreateDeleteEntities.md +++ b/sdk/tables/Azure.Data.Tables/samples/Sample2CreateDeleteEntities.md @@ -29,6 +29,7 @@ Define a strongly-typed entity class that extends from the `ITableEntity` interf ```C# Snippet:TablesSample2DefineStronglyTypedEntity // Define a strongly typed entity by extending the class. + public class OfficeSupplyEntity : ITableEntity { public string Product { get; set; } @@ -45,6 +46,7 @@ Once defined, create an entity with the class. ```C# Snippet:TablesSample2CreateStronglyTypedEntity // Create an instance of the strongly-typed entity and set their properties. + var strongEntity = new OfficeSupplyEntity { PartitionKey = partitionKey, @@ -64,6 +66,7 @@ Properties accessed using the indexer `[]` will be typed as an `object`, but `Ta ```C# Snippet:TablesSample2CreateDictionaryEntity // Make a dictionary entity by defining a . + var entity = new TableEntity(partitionKey, rowKey) { { "Product", "Marker Set" }, @@ -79,6 +82,7 @@ To add the entity to the table, invoke `CreateEntity` and pass in the newly crea ```C# Snippet:TablesSample2AddEntity // Add the newly created entity. + tableClient.AddEntity(entity); ``` @@ -87,6 +91,7 @@ To delete an entity, invoke `DeleteEntity` and pass in its partition and row key ```C# Snippet:TablesSample2DeleteEntity // Delete the entity given the partition and row key. + tableClient.DeleteEntity(partitionKey, rowKey); ``` --- diff --git a/sdk/tables/Azure.Data.Tables/samples/Sample3QueryTables.md b/sdk/tables/Azure.Data.Tables/samples/Sample3QueryTables.md index 8abfed4ac7299..c6882b87a1a77 100644 --- a/sdk/tables/Azure.Data.Tables/samples/Sample3QueryTables.md +++ b/sdk/tables/Azure.Data.Tables/samples/Sample3QueryTables.md @@ -8,6 +8,7 @@ In the sample below, we will use a Storage account and authenticate with an endp ```C# Snippet:TablesSample1CreateClient // Construct a new using a . + var serviceClient = new TableServiceClient( new Uri(storageUri), new TableSharedKeyCredential(accountName, storageAccountKey)); @@ -18,10 +19,13 @@ To get a collection of tables, call `GetTables` and optionally pass in an OData ```C# Snippet:TablesSample3QueryTables // Use the to query the service. Passing in OData filter strings is optional. + Pageable queryTableResults = serviceClient.GetTables(filter: $"TableName eq '{tableName}'"); Console.WriteLine("The following are the names of the tables in the query results:"); + // Iterate the in order to access queried tables. + foreach (TableItem table in queryTableResults) { Console.WriteLine(table.TableName); diff --git a/sdk/tables/Azure.Data.Tables/samples/Sample4QueryEntities.md b/sdk/tables/Azure.Data.Tables/samples/Sample4QueryEntities.md index d3c6ef466a746..04528c2adaf1e 100644 --- a/sdk/tables/Azure.Data.Tables/samples/Sample4QueryEntities.md +++ b/sdk/tables/Azure.Data.Tables/samples/Sample4QueryEntities.md @@ -31,6 +31,7 @@ Here is a query returning a collection of dictionary `TableEntity` objects that Pageable queryResultsFilter = tableClient.Query(filter: $"PartitionKey eq '{partitionKey}'"); // Iterate the to access all queried entities. + foreach (TableEntity qEntity in queryResultsFilter) { Console.WriteLine($"{qEntity.GetString("Product")}: {qEntity.GetDouble("Price")}"); @@ -46,6 +47,7 @@ To define the strongly-typed class, refer to the sample on [creating classes](Sa ```C# Snippet:TablesSample4QueryEntitiesExpression // Use the to query the table using a filter expression. + double priceCutOff = 6.00; Pageable queryResultsLINQ = tableClient.Query(ent => ent.Price >= priceCutOff); ``` @@ -54,7 +56,7 @@ Pageable queryResultsLINQ = tableClient.Query queryResultsSelect = tableClient.Query(select: new List() { "Product", "Price"}); +Pageable queryResultsSelect = tableClient.Query(select: new List() { "Product", "Price" }); ``` ## Query entities with `maxPerPage` @@ -66,6 +68,7 @@ To query entities by page, call `Query`, specify the desired entity return type, Pageable queryResultsMaxPerPage = tableClient.Query(maxPerPage: 10); // Iterate the by page. + foreach (Page page in queryResultsMaxPerPage.AsPages()) { Console.WriteLine("This is a new page!"); diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs new file mode 100644 index 0000000000000..3f05bd2258e60 --- /dev/null +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core.TestFramework; +using NUnit.Framework; +using Azure.Data.Tables.Tests; +using Azure.Data.Tables.Sas; +using System.Threading.Tasks; + +namespace Azure.Data.Tables.Samples +{ + [LiveOnly] + public partial class TablesSamples : TablesTestEnvironment + { + [Test] + public async Task ConnStringAuth() + { + string tableName = "OfficeSupplies"; + string connectionString = $"DefaultEndpointsProtocol=https;AccountName={StorageAccountName};AccountKey={PrimaryStorageAccountKey};EndpointSuffix={StorageEndpointSuffix ?? DefaultStorageSuffix}"; + + #region Snippet:TablesAuthConnString + // Construct a new TableClient using a connection string. + + var client = new TableClient( + connectionString, + tableName); + + // Create the table if it doesn't already exist to verify we've successfully authenticated. + + await client.CreateIfNotExistsAsync(); + #endregion + } + + [Test] + public async Task SharedKeyAuth() + { + string storageUri = StorageUri; + string accountName = StorageAccountName; + string accountKey = PrimaryStorageAccountKey; + string tableName = "OfficeSupplies"; + + #region Snippet:TablesAuthSharedKey + // Construct a new TableClient using a TableSharedKeyCredential. + + var client = new TableClient( + new Uri(storageUri), + tableName, + new TableSharedKeyCredential(accountName, accountKey)); + + // Create the table if it doesn't already exist to verify we've successfully authenticated. + + await client.CreateIfNotExistsAsync(); + #endregion + } + + [Test] + public async Task SasAuth() + { + string storageUri = StorageUri; + string accountName = StorageAccountName; + string accountKey = PrimaryStorageAccountKey; + string tableName = "OfficeSupplies"; + + #region Snippet:TablesAuthSas + // Construct a new using a . + + var credential = new TableSharedKeyCredential(accountName, accountKey); + + var serviceClient = new TableServiceClient( + new Uri(storageUri), + credential); + + // Build a shared access signature with the Write and Delete permissions and access to all service resource types. + + TableAccountSasBuilder sasWriteDelete = serviceClient.GetSasBuilder(TableAccountSasPermissions.Write, TableAccountSasResourceTypes.All, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); + string tokenWriteDelete = sasWriteDelete.Sign(credential); + + // Build SAS URIs. + + UriBuilder sasUriWriteDelete = new UriBuilder(storageUri) + { + Query = tokenWriteDelete + }; + + // Create the TableServiceClients using the SAS URIs. + + var serviceClientWithSas = new TableServiceClient(sasUriWriteDelete.Uri); + + // Validate that we are able to create a table using the SAS URI with Write and Delete permissions. + + await serviceClientWithSas.CreateTableIfNotExistsAsync(tableName); + + // Validate that we are able to delete a table using the SAS URI with Write and Delete permissions. + + await serviceClientWithSas.DeleteTableAsync(tableName); + #endregion + } + } +} diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample1_CreateDeleteTable.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample1_CreateDeleteTable.cs index bc23a68deb60a..ed54d8529d482 100644 --- a/sdk/tables/Azure.Data.Tables/tests/samples/Sample1_CreateDeleteTable.cs +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample1_CreateDeleteTable.cs @@ -22,6 +22,7 @@ public void CreateDeleteTable() #region Snippet:TablesSample1CreateClient // Construct a new using a . + var serviceClient = new TableServiceClient( new Uri(storageUri), new TableSharedKeyCredential(accountName, storageAccountKey)); @@ -29,12 +30,14 @@ public void CreateDeleteTable() #region Snippet:TablesSample1CreateTable // Create a new table. The class stores properties of the created table. + TableItem table = serviceClient.CreateTable(tableName); Console.WriteLine($"The created table's name is {table.TableName}."); #endregion #region Snippet:TablesSample1DeleteTable // Deletes the table made previously. + serviceClient.DeleteTable(tableName); #endregion diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample2_CreateDeleteEntities.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample2_CreateDeleteEntities.cs index ddb15de746793..b6cd3292b65ea 100644 --- a/sdk/tables/Azure.Data.Tables/tests/samples/Sample2_CreateDeleteEntities.cs +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample2_CreateDeleteEntities.cs @@ -25,6 +25,7 @@ public void CreateDeleteEntity() #region Snippet:TablesSample2CreateTableWithTableClient // Construct a new using a . + var tableClient = new TableClient( new Uri(storageUri), tableName, @@ -36,6 +37,7 @@ public void CreateDeleteEntity() #region Snippet:TablesSample2CreateDictionaryEntity // Make a dictionary entity by defining a . + var entity = new TableEntity(partitionKey, rowKey) { { "Product", "Marker Set" }, @@ -48,11 +50,13 @@ public void CreateDeleteEntity() #region Snippet:TablesSample2AddEntity // Add the newly created entity. + tableClient.AddEntity(entity); #endregion #region Snippet:TablesSample2CreateStronglyTypedEntity // Create an instance of the strongly-typed entity and set their properties. + var strongEntity = new OfficeSupplyEntity { PartitionKey = partitionKey, @@ -66,10 +70,12 @@ public void CreateDeleteEntity() #endregion // Add the newly created entity. + tableClient.AddEntity(strongEntity); #region Snippet:TablesSample2DeleteEntity // Delete the entity given the partition and row key. + tableClient.DeleteEntity(partitionKey, rowKey); #endregion @@ -80,6 +86,7 @@ public void CreateDeleteEntity() #region Snippet:TablesSample2DefineStronglyTypedEntity // Define a strongly typed entity by extending the class. + public class OfficeSupplyEntity : ITableEntity { public string Product { get; set; } diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample3_QueryTables.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample3_QueryTables.cs index a5a7d8fd7ffd5..68399e4c93477 100644 --- a/sdk/tables/Azure.Data.Tables/tests/samples/Sample3_QueryTables.cs +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample3_QueryTables.cs @@ -28,10 +28,13 @@ public void QueryTables() #region Snippet:TablesSample3QueryTables // Use the to query the service. Passing in OData filter strings is optional. + Pageable queryTableResults = serviceClient.GetTables(filter: $"TableName eq '{tableName}'"); Console.WriteLine("The following are the names of the tables in the query results:"); + // Iterate the in order to access queried tables. + foreach (TableItem table in queryTableResults) { Console.WriteLine(table.TableName); diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample4_QueryEntities.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample4_QueryEntities.cs index 3aee20a423c4d..2a029b1fa8c5a 100644 --- a/sdk/tables/Azure.Data.Tables/tests/samples/Sample4_QueryEntities.cs +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample4_QueryEntities.cs @@ -52,6 +52,7 @@ public void QueryEntities() Pageable queryResultsFilter = tableClient.Query(filter: $"PartitionKey eq '{partitionKey}'"); // Iterate the to access all queried entities. + foreach (TableEntity qEntity in queryResultsFilter) { Console.WriteLine($"{qEntity.GetString("Product")}: {qEntity.GetDouble("Price")}"); @@ -62,18 +63,20 @@ public void QueryEntities() #region Snippet:TablesSample4QueryEntitiesExpression // Use the to query the table using a filter expression. + double priceCutOff = 6.00; Pageable queryResultsLINQ = tableClient.Query(ent => ent.Price >= priceCutOff); #endregion #region Snippet:TablesSample4QueryEntitiesSelect - Pageable queryResultsSelect = tableClient.Query(select: new List() { "Product", "Price"}); + Pageable queryResultsSelect = tableClient.Query(select: new List() { "Product", "Price" }); #endregion #region Snippet:TablesSample4QueryEntitiesMaxPerPage Pageable queryResultsMaxPerPage = tableClient.Query(maxPerPage: 10); // Iterate the by page. + foreach (Page page in queryResultsMaxPerPage.AsPages()) { Console.WriteLine("This is a new page!"); diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample6_Troubleshooting.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample6_Troubleshooting.cs new file mode 100644 index 0000000000000..3f353e7efea06 --- /dev/null +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample6_Troubleshooting.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core.TestFramework; +using NUnit.Framework; +using Azure.Data.Tables.Tests; +using Azure.Data.Tables.Sas; +using System.Threading.Tasks; +using System.Net; + +namespace Azure.Data.Tables.Samples +{ + [LiveOnly] + public partial class TablesSamples : TablesTestEnvironment + { + [Test] + public void CreateTableConflict() + { + string tableName = "OfficeSupplies"; + string connectionString = $"DefaultEndpointsProtocol=https;AccountName={StorageAccountName};AccountKey={PrimaryStorageAccountKey};EndpointSuffix={StorageEndpointSuffix ?? DefaultStorageSuffix}"; + + #region Snippet:CreateDuplicateTable + // Construct a new TableClient using a connection string. + + var client = new TableClient( + connectionString, + tableName); + + // Create the table if it doesn't already exist. + + client.CreateIfNotExists(); + + // Now attempt to create the same table unconditionally. + + try + { + client.Create(); + } + catch (RequestFailedException ex) when (ex.Status == (int)HttpStatusCode.Conflict) + { + Console.WriteLine(ex.ToString()); + } + #endregion + } + } +} From cb413eb36eca92f87f1d114bf5b89de374cba120 Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 31 Aug 2020 14:48:11 -0500 Subject: [PATCH 2/3] typos --- sdk/tables/Azure.Data.Tables/readme.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/tables/Azure.Data.Tables/readme.md b/sdk/tables/Azure.Data.Tables/readme.md index d38d5b5218846..bd3a552fa260f 100644 --- a/sdk/tables/Azure.Data.Tables/readme.md +++ b/sdk/tables/Azure.Data.Tables/readme.md @@ -38,7 +38,7 @@ az storage account create -n mystorageaccount -g MyResourceGroup -l westus --sub #### Creating a Cosmos DB -Create a storage account `MyCosmosDBDatabaseAccount` in resource group `MyResourceGroup` +Create a Cosmos DB account `MyCosmosDBDatabaseAccount` in resource group `MyResourceGroup` in the subscription `MySubscription` and a table named `MyTableName` in the account. ``` @@ -98,7 +98,7 @@ foreach (TableItem table in queryTableResults) } ``` -Individual tables can bel deleted from the service. +Individual tables can be deleted from the service. ```C# Snippet:TablesSample1DeleteTable // Deletes the table made previously. @@ -171,9 +171,9 @@ tableClient.DeleteEntity(partitionKey, rowKey); ## Troubleshooting When you interact with the Azure table library using the .NET SDK, errors returned by the service correspond to the same HTTP -status codes returned for [REST API][keyvault_rest] requests. +status codes returned for [REST API][tables_rest] requests. -For example, if you try to retrieve a create a table that already exists, a `409` error is returned, indicating "Conflict". +For example, if you try to create a table that already exists, a `409` error is returned, indicating "Conflict". ```C# Snippet:CreateDuplicateTable // Construct a new TableClient using a connection string. @@ -215,6 +215,7 @@ For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments. +[tables_rest]: https://docs.microsoft.com/en-us/rest/api/storageservices/table-service-rest-api [azure_cli]: https://docs.microsoft.com/cli/azure [azure_sub]: https://azure.microsoft.com/free/ [contrib]: ./CONTRIBUTING.md From c03149a585c8661012954dc182e89ed73121250f Mon Sep 17 00:00:00 2001 From: Christopher Scott Date: Mon, 31 Aug 2020 14:51:17 -0500 Subject: [PATCH 3/3] add Delete perms --- sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md | 2 +- sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md b/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md index a58a716e2c182..e10869067dcc9 100644 --- a/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md +++ b/sdk/tables/Azure.Data.Tables/samples/Sample0Auth.md @@ -89,7 +89,7 @@ var serviceClient = new TableServiceClient( // Build a shared access signature with the Write and Delete permissions and access to all service resource types. -TableAccountSasBuilder sasWriteDelete = serviceClient.GetSasBuilder(TableAccountSasPermissions.Write, TableAccountSasResourceTypes.All, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); +TableAccountSasBuilder sasWriteDelete = serviceClient.GetSasBuilder(TableAccountSasPermissions.Write | TableAccountSasPermissions.Delete, TableAccountSasResourceTypes.All, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); string tokenWriteDelete = sasWriteDelete.Sign(credential); // Build SAS URIs. diff --git a/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs b/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs index 3f05bd2258e60..61c9a4096ad40 100644 --- a/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs +++ b/sdk/tables/Azure.Data.Tables/tests/samples/Sample0_Auth.cs @@ -73,7 +73,7 @@ public async Task SasAuth() // Build a shared access signature with the Write and Delete permissions and access to all service resource types. - TableAccountSasBuilder sasWriteDelete = serviceClient.GetSasBuilder(TableAccountSasPermissions.Write, TableAccountSasResourceTypes.All, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); + TableAccountSasBuilder sasWriteDelete = serviceClient.GetSasBuilder(TableAccountSasPermissions.Write | TableAccountSasPermissions.Delete, TableAccountSasResourceTypes.All, new DateTime(2040, 1, 1, 1, 1, 0, DateTimeKind.Utc)); string tokenWriteDelete = sasWriteDelete.Sign(credential); // Build SAS URIs.