Table Storage Bindings

Azure Table storage is a NoSQL key-attribute store that you can use to store large amounts of structured, non-relational data. Azure Functions provides built-in bindings for interacting with Azure Table Storage, simplifying common operations like reading entities and writing entities.

Table Storage Input Binding

The Table storage input binding allows you to retrieve entities from an Azure Table. You can retrieve a single entity by its partition key and row key, or retrieve multiple entities that match a query.

Single Entity Retrieval

To retrieve a single entity, you need to specify the partitionKey and rowKey. The binding will automatically deserialize the entity into a strongly-typed object or a dictionary.

// C# Example
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using System.Threading.Tasks;

public static class GetEntity
{
    public static async Task Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "items/{partitionKey}/{rowKey}")] HttpRequest req,
        string partitionKey,
        string rowKey,
        [Table("MyTable", partitionKey, rowKey)] MyEntity entity,
        TraceWriter log)
    {
        if (entity == null)
        {
            log.Info($"Item not found with PartitionKey='{partitionKey}' and RowKey='{rowKey}'.");
            // Return appropriate HTTP response
        }
        else
        {
            log.Info($"Retrieved entity: {entity.Name}");
            // Process the entity
        }
    }

    public class MyEntity
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
    }
}

In the example above:

Querying Multiple Entities

You can use OData filter syntax to query for multiple entities. The result will be a collection of entities.

// C# Example
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using System.Collections.Generic;
using System.Linq;

public static class QueryEntities
{
    public static void Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "items/query")] HttpRequest req,
        [Table("MyTable", "Partition1", "{Query}")] IQueryable entities,
        TraceWriter log)
    {
        // The '{Query}' placeholder in the route is not directly used for OData filters with Table binding.
        // Actual OData filtering is typically applied by the binding itself when a filter is specified.
        // For direct querying with filters, consider TableServiceClient SDK or a Table output binding with query.

        // Example of iterating through results IF the binding was configured for it or using TableServiceClient:
        if (entities != null && entities.Any())
        {
            foreach (var entity in entities)
            {
                log.Info($"Found: {entity.Name}");
            }
            // Return results
        }
        else
        {
            log.Info("No entities found matching the criteria.");
            // Return appropriate HTTP response
        }
    }

    public class MyEntity
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
    }
}
Note on Querying: Directly applying OData filter strings as parameters to the [Table] input binding can be complex. For more advanced querying, it's often recommended to use the Azure SDK for .NET (TableServiceClient) within your function code.

Table Storage Output Binding

The Table storage output binding allows you to write data to an Azure Table. You can insert or update single entities, or perform batch operations.

Inserting/Updating a Single Entity

To insert or update an entity, you bind a parameter to an output binding. If an entity with the same partition key and row key already exists, it will be updated; otherwise, it will be inserted.

// C# Example
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;

public static class SaveEntity
{
    public static void Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] MyEntity inputEntity,
        [Table("MyTable")] out MyEntity outputEntity,
        TraceWriter log)
    {
        outputEntity = inputEntity;
        log.Info($"Saved entity with PartitionKey='{inputEntity.PartitionKey}' and RowKey='{inputEntity.RowKey}'.");
    }

    public class MyEntity
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
    }
}

In this example:

Batch Operations

For efficiency, you can perform batch operations to insert, update, or delete multiple entities in a single request.

// C# Example using TableClient
using Microsoft.Azure.Functions.Worker;
using Azure.Data.Tables;
using System.Collections.Generic;
using System.Linq;

public class BatchOperationFunction
{
    private readonly TableServiceClient _tableServiceClient;

    public BatchOperationFunction(TableServiceClient tableServiceClient)
    {
        _tableServiceClient = tableServiceClient;
    }

    [Function("BatchTableOperation")]
    public async Task Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] IEnumerable entities,
        FunctionContext context)
    {
        var tableClient = _tableServiceClient.GetTableClient("MyTable");
        var batch = tableClient.CreateEntityGroupBatch();

        foreach (var entity in entities)
        {
            batch.AddEntity(entity);
        }

        await tableClient.SubmitTransactionAsync(batch);
    }

    public class MyEntity : ITableEntity
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
        public string Name { get; set; }
        public int Value { get; set; }
        public Azure.ETag ETag { get; set; }
        public DateTimeOffset? Timestamp { get; set; }
    }
}
Tip: When using the newer Azure SDKs (Azure.Data.Tables), you'll typically inject TableServiceClient into your function and perform operations using the SDK directly. This offers more flexibility and control compared to the older attribute-based output bindings.

Configuration

Table storage bindings require a connection string to your Azure Storage account. This is typically configured in your local.settings.json file for local development and in the Application Settings of your Azure Function App in the portal.

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true", // Or your actual storage connection string
    "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "UseDevelopmentStorage=true", // Or your actual storage connection string
    "MyStorageConnection": "DefaultEndpointsProtocol=https;AccountName=YOUR_ACCOUNT_NAME;AccountKey=YOUR_ACCOUNT_KEY;EndpointSuffix=core.windows.net"
  }
}

The connection string can be named anything, but it's common to use names like AzureWebJobsStorage (for Functions runtime) or a custom name like MyStorageConnection which you then reference in your binding attributes.

// Example referencing a custom connection string
[Table("MyTable", Connection = "MyStorageConnection")] out MyEntity outputEntity

Common Scenarios