Get started with Azure Table Storage and the Azure Cosmos DB Table api using F#
Azure Table Storage is a service that stores structured NoSQL data in the cloud. Table storage is a key/attribute store with a schemaless design. Because Table storage is schemaless, it's easy to adapt your data as the needs of your application evolve. Access to data is fast and cost-effective for all kinds of applications. Table storage is typically significantly lower in cost than traditional SQL for similar volumes of data.
You can use Table storage to store flexible datasets, such as user data for web applications, address books, device information, and any other type of metadata that your service requires. You can store any number of entities in a table, and a storage account may contain any number of tables, up to the capacity limit of the storage account.
Azure Cosmos DB provides the Table API for applications that are written for Azure Table Storage and that require premium capabilities such as:
- Turnkey global distribution.
- Dedicated throughput worldwide.
- Single-digit millisecond latencies at the 99th percentile.
- Guaranteed high availability.
- Automatic secondary indexing.
Applications written for Azure Table Storage can migrate to Azure Cosmos DB by using the Table API with no code changes and take advantage of premium capabilities. The Table API has client SDKs available for .NET, Java, Python, and Node.js.
For more information, see Introduction to Azure Cosmos DB Table API.
About this tutorial
This tutorial shows how to write F# code to do some common tasks using Azure Table Storage or the Azure Cosmos DB Table API, including creating and deleting a table and inserting, updating, deleting, and querying table data.
Prerequisites
To use this guide, you must first create an Azure storage account or Azure Cosmos DB account.
Create an F# script and start F# interactive
The samples in this article can be used in either an F# application or an F# script. To create an F# script, create a file with the .fsx
extension, for example, tables.fsx
, in your F# development environment.
How to execute scripts
F# Interactive, dotnet fsi
, can be launched interactively, or it can be launched from the command line to run a script. The command-line syntax is
> dotnet fsi [options] [ script-file [arguments] ]
Add packages in a script
Next, use #r
nuget:package name
to install the Azure.Data.Tables
package and open
namespaces. Such as
> #r "nuget: Azure.Data.Tables"
open Azure.Data.Tables
Add namespace declarations
Add the following open
statements to the top of the tables.fsx
file:
open System
open Azure
open Azure.Data.Tables // Namespace for Table storage types
Get your Azure Storage connection string
If you're connecting to Azure Storage Table service, you'll need your connection string for this tutorial. You can copy your connection string from the Azure portal. For more information about connection strings, see Configure Storage Connection Strings.
Get your Azure Cosmos DB connection string
If you're connecting to Azure Cosmos DB, you'll need your connection string for this tutorial. You can copy your connection string from the Azure portal. In the Azure portal, in your Cosmos DB account, go to Settings > Connection String, and select the Copy button to copy your Primary Connection String.
For the tutorial, enter your connection string in your script, like the following example:
let storageConnString = "UseDevelopmentStorage=true" // fill this in from your storage account
Create the table service client
The TableServiceClient
class enables you to retrieve tables and entities in Table storage. Here's one way to create the service client:
let tableClient = TableServiceClient storageConnString
Now you are ready to write code that reads data from and writes data to Table storage.
Create a table
This example shows how to create a table if it does not already exist:
// Retrieve a reference to the table.
let table = tableClient.GetTableClient "people"
// Create the table if it doesn't exist.
table.CreateIfNotExists () |> ignore
Add an entity to a table
An entity has to have a type that implements ITableEntity
. You can extend ITableEntity
in any way you like, but your type must have a parameter-less constructor. Only properties that have both get
and set
are stored in your Azure Table.
An entity's partition and row key uniquely identify the entity in the table. Entities with the same partition key can be queried faster than those with different partition keys, but using diverse partition keys allows for greater scalability of parallel operations.
Here's an example of a Customer
that uses the lastName
as the partition key and the firstName
as the row key.
type Customer (firstName, lastName, email: string, phone: string) =
interface ITableEntity with
member val ETag = ETag "" with get, set
member val PartitionKey = "" with get, set
member val RowKey = "" with get, set
member val Timestamp = Nullable() with get, set
new() = Customer(null, null, null, null)
member val Email = email with get, set
member val PhoneNumber = phone with get, set
member val PartitionKey = lastName with get, set
member val RowKey = firstName with get, set
Now add Customer
to the table. To do so, we can use the AddEntity() method.
let customer = Customer ("Walter", "Harp", "Walter@contoso.com", "425-555-0101")
table.AddEntity customer
Insert a batch of entities
You can insert a batch of entities into a table using a single write operation. Batch operations allow you to combine operations into a single execution, but they have some restrictions:
- You can perform updates, deletes, and inserts in the same batch operation.
- A batch operation can include up to 100 entities.
- All entities in a batch operation must have the same partition key.
- While it is possible to perform a query in a batch operation, it must be the only operation in the batch.
Here's some code that combines two inserts into a batch operation:
let customers =
[
Customer("Jeff", "Smith", "Jeff@contoso.com", "425-555-0102")
Customer("Ben", "Smith", "Ben@contoso.com", "425-555-0103")
]
// Add the entities to be added to the batch and submit it in a transaction.
customers
|> List.map (fun customer -> TableTransactionAction (TableTransactionActionType.Add, customer))
|> table.SubmitTransaction
Retrieve all entities in a partition
To query a table for all entities in a partition, use a Query<T>
object. Here, you filter for entities where "Smith" is the partition key.
table.Query<Customer> "PartitionKey eq 'Smith'"
Retrieve a range of entities in a partition
If you don't want to query all the entities in a partition, you can specify a range by combining the partition key filter with a row key filter. Here, you use two filters to get all entities in the "Smith" partition where the row key (first name) starts with a letter earlier than "M" in the alphabet.
table.Query<Customer> "PartitionKey eq 'Smith' and RowKey lt 'J'"
Retrieve a single entity
To retrieve a single, specific entity, use GetEntityAsync
to specify the customer "Ben Smith". Instead of a collection, you get back a Customer
. Specifying both the partition key and the row key in a query is the fastest way to retrieve a single entity from the Table service.
let singleResult = table.GetEntity<Customer>("Smith", "Ben").Value
You now print the results:
// Evaluate this value to print it out into the F# Interactive console
singleResult
Update an entity
To update an entity, retrieve it from the Table service, modify the entity object, and then save the changes back to the Table service using a TableUpdateMode.Replace
operation. This causes the entity to be fully replaced on the server, unless the entity on the server has changed since it was retrieved, in which case the operation fails. This failure is to prevent your application from inadvertently overwriting changes from other sources.
singleResult.PhoneNumber <- "425-555-0103"
try
table.UpdateEntity (singleResult, ETag "", TableUpdateMode.Replace) |> ignore
printfn "Update succeeded"
with
| :? RequestFailedException as e ->
printfn $"Update failed: {e.Status} - {e.ErrorCode}"
Upsert an entity
Sometimes, you don't know whether an entity exists in the table. And if it does, the current values stored in it are no longer needed. You can use UpsertEntity
method to create the entity or replace it if it exists, regardless of its state.
singleResult.PhoneNumber <- "425-555-0104"
table.UpsertEntity (singleResult, TableUpdateMode.Replace)
Query a subset of entity properties
A table query can retrieve just a few properties from an entity instead of all of them. This technique, called projection, can improve query performance, especially for large entities. Here, you return only email addresses using Query<T>
and Select
. Projection is not supported on the local storage emulator, so this code runs only when you're using an account on the Table service.
query {
for customer in table.Query<Customer> () do
select customer.Email
}
Retrieve entities in pages asynchronously
If you are reading a large number of entities, and you want to process them as they are retrieved rather than waiting for them all to return, you can use a segmented query. Here, you return results in pages by using an async workflow so that execution is not blocked while you're waiting for a large set of results to return.
let pagesResults = table.Query<Customer> ()
for page in pagesResults.AsPages () do
printfn "This is a new page!"
for customer in page.Values do
printfn $"customer: {customer.RowKey} {customer.PartitionKey}"
Delete an entity
You can delete an entity after you have retrieved it. As with updating an entity, this fails if the entity has changed since you retrieved it.
table.DeleteEntity ("Smith", "Ben")
Delete a table
You can delete a table from a storage account. A table that has been deleted will be unavailable to be re-created for some time following the deletion.
table.Delete ()
See also
Feedback
Submit and view feedback for