{"id":18602996,"url":"https://github.com/devlooped/tablestorage","last_synced_at":"2025-04-10T19:31:21.652Z","repository":{"id":37940923,"uuid":"364339260","full_name":"devlooped/TableStorage","owner":"devlooped","description":"Repository pattern with POCO object support for storing to Azure / Cosmos DB Table Storage","archived":false,"fork":false,"pushed_at":"2025-03-18T00:25:22.000Z","size":1085,"stargazers_count":27,"open_issues_count":4,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-25T03:34:03.755Z","etag":null,"topics":["azure","cosmosdb","storage","table"],"latest_commit_sha":null,"homepage":"https://clarius.org/TableStorage","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devlooped.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":".github/FUNDING.yml","license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"devlooped"}},"created_at":"2021-05-04T17:45:47.000Z","updated_at":"2025-03-01T20:40:21.000Z","dependencies_parsed_at":"2024-01-25T22:28:02.343Z","dependency_job_id":"2e897fb9-9402-4011-81b0-237265fde193","html_url":"https://github.com/devlooped/TableStorage","commit_stats":null,"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FTableStorage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FTableStorage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FTableStorage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devlooped%2FTableStorage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devlooped","download_url":"https://codeload.github.com/devlooped/TableStorage/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248281400,"owners_count":21077423,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure","cosmosdb","storage","table"],"created_at":"2024-11-07T02:13:11.325Z","updated_at":"2025-04-10T19:31:16.640Z","avatar_url":"https://github.com/devlooped.png","language":"C#","funding_links":["https://github.com/sponsors/devlooped","https://github.com/sponsors"],"categories":[],"sub_categories":[],"readme":"![Icon](https://raw.githubusercontent.com/devlooped/TableStorage/main/assets/img/icon-32.png) TableStorage\n============\n\n[![Version](https://img.shields.io/nuget/v/Devlooped.TableStorage.svg?color=royalblue)](https://www.nuget.org/packages/Devlooped.TableStorage) \n[![Downloads](https://img.shields.io/nuget/dt/Devlooped.TableStorage.svg?color=green)](https://www.nuget.org/packages/Devlooped.TableStorage) \n[![License](https://img.shields.io/github/license/devlooped/TableStorage.svg?color=blue)](https://github.com/devlooped/TableStorage/blob/main/license.txt) \n[![Build](https://github.com/devlooped/TableStorage/workflows/build/badge.svg?branch=main)](https://github.com/devlooped/TableStorage/actions)\n\n\u003c!-- include https://github.com/devlooped/.github/raw/main/sponsorlink.md --\u003e\n*This project uses [SponsorLink](https://github.com/devlooped#sponsorlink) \nand may issue IDE-only warnings if no active sponsorship is detected.*\n\n\u003c!-- https://github.com/devlooped/.github/raw/main/sponsorlink.md --\u003e\n\u003c!-- #content --\u003e\nRepository pattern with POCO object support for storing to Azure/CosmosDB Table Storage\n\n![Screenshot of basic usage](https://raw.githubusercontent.com/devlooped/TableStorage/main/assets/img/tablestorage.png)\n\n\u003e NOTE: This library is a thin wrapper around the latest Azure SDK v12+ for Table Storage,\n\u003e and uses [CloudStorageAccount](https://www.nuget.org/packages/Devlooped.CloudStorageAccount) which \n\u003e is a 100% backwards compatible implementation of the Azure SDK v11 `CloudStorageAccount` class.\n\n## Usage\n\nGiven an entity like:\n\n```csharp\npublic record Product(string Category, string Id) \n{\n  public required string? Title { get; init; }\n  public double Price { get; init; }\n  public DateOnly CreatedAt { get; init; }\n}\n```\n\n\u003e NOTE: entity can have custom constructor, key properties can be read-only \n\u003e (Category and Id in this case for example), and it doesn't need to inherit \n\u003e from anything, implement any interfaces or use \n\u003e any custom attributes (unless you want to). As shown above, it can even be \n\u003e a simple record type.\n\nThe entity can be stored and retrieved with:\n\n```csharp\nvar storageAccount = CloudStorageAccount.DevelopmentStorageAccount; // or production one\n// We lay out the parameter names for clarity only.\nvar repo = TableRepository.Create\u003cProduct\u003e(storageAccount, \n    tableName: \"Products\",\n    partitionKey: p =\u003e p.Category, \n    rowKey: p =\u003e p.Id);\n\nvar product = new Product(\"book\", \"1234\") \n{\n  Title = \"Table Storage is Cool\",\n  Price = 25.5,\n};\n\n// Insert or Update behavior (aka \"upsert\")\nawait repo.PutAsync(product);\n\n// Enumerate all products in category \"book\"\nawait foreach (var p in repo.EnumerateAsync(\"book\"))\n   Console.WriteLine(p.Price);\n\n// Query books priced in the 20-50 range, \n// project just title + price\nawait foreach (var info in from prod in repo.CreateQuery()\n                           where prod.Price \u003e= 20 and prod.Price \u003c= 50\n                           select new { prod.Title, prod.Price })\n  Console.WriteLine($\"{info.Title}: {info.Price}\");\n\n// Get previously saved product.\nProduct saved = await repo.GetAsync(\"book\", \"1234\");\n\n// Delete product\nawait repo.DeleteAsync(\"book\", \"1234\");\n\n// Can also delete passing entity\nawait repo.DeleteAsync(saved);\n```\n\nAttributes can also be used to eliminate the need for lambdas altogether when \nthe entity storage layout is known at compile time:\n\n```csharp\n[Table(\"Products\")]\npublic record Product([PartitionKey] string Category, [RowKey] string Id) ... \n\nvar storageAccount = CloudStorageAccount.DevelopmentStorageAccount;\n// Everything discovered from attributes.\nvar repo = TableRepository.Create\u003cProduct\u003e(storageAccount);\n```\n\nSee the [Attributes](#attributes) section below for more details on how to use them.\n\nIf the product were books for example, it might make sense to partition by author. \nIn that case, you could use a `TableRepository\u003cBook\u003e` when saving:\n\n```csharp\npublic record Book([RowKey] string ISBN, string Title, string Author, BookFormat Format, int Pages);\n\nvar repo = TableRepository.Create\u003cProduct\u003e(storageAccount, \"Books\",\n  partitionKey: x =\u003e x.Author);\n\nawait repo.PutAsync(book);\n```\n\n\u003e Note how you can mix and match attributes and explicit lambdas as needed. \n\u003e The latter takes precedence over the former.\n\nAnd later on when listing/filtering books by a particular author, you can use \na `TablePartition\u003cProduct\u003e` so all querying is automatically scoped to that author:\n\n```csharp\nvar partition = TablePartition.Create\u003cBook\u003e(storageAccount, \"Books\", \n  partitionKey: \"Rick Riordan\");\n\n// Get Rick Riordan books, only from Disney/Hyperion, with over 1000 pages\nvar query = from book in repo.CreateQuery()\n            where \n                book.ISBN.CompareTo(\"97814231\") \u003e= 0 \u0026\u0026\n                book.ISBN.CompareTo(\"97814232\") \u003c 0 \u0026\u0026 \n                book.Pages \u003e= 1000\n            select new { book.ISBN, book.Title };\n```\n\nUsing table partitions is quite convenient for handling reference data too, for example.\nEnumerating all entries in the partition wouldn't be something you'd typically do for \nyour \"real\" data, but for reference data, it could come in handy.\n\n\u003e NOTE: if access to the `Timestamp` managed by Azure Table Storage for the entity is needed, \n\u003e just declare a property with that name with either `DateTimeOffset`, `DateTime` or `string` type\n\u003e to read it.\n\nStored entities using `TableRepository` and `TablePartition` use individual columns for \nproperties, which makes it easy to browse the data (and query, as shown above!). \n\n\u003e NOTE: partition and row keys can also be typed as `Guid`\n\nDocument-based storage is also available via `DocumentRepository` and `DocumentPartition` if \nyou don't need the individual columns.\n\n\u003c!-- #documents --\u003e\n## Document Storage\n\nThe `DocumentRepository.Create` and `DocumentPartition.Create` factory methods provide access \nto document-based storage, exposing the a similar API as column-based storage. \n\nDocument repositories cause entities to be persisted as a single document column, alongside type and version \ninformation to handle versioning a the app level as needed. \n\nThe API is mostly the same as for column-based repositories (document repositories implement \nthe same underlying `ITableStorage` interface):\n\n```csharp\npublic record Product(string Category, string Id) \n{\n  public string? Title { get; init; }\n  public double Price { get; init; }\n  public DateOnly CreatedAt { get; init; }\n}\n\nvar book = new Product(\"book\", \"9781473217386\")\n{\n    Title = \"Neuromancer\",\n    Price = 7.32\n};\n\n// Column-based storage\nvar repo = TableRepository.Create\u003cProduct\u003e(\n    CloudStorageAccount.DevelopmentStorageAccount,\n    tableName: \"Products\",\n    partitionKey: p =\u003e p.Category,\n    rowKey: p =\u003e p.Id);\n\nawait repo.PutAsync(book);\n\n// Document-based storage\nvar docs = DocumentRepository.Create\u003cProduct\u003e(\n    CloudStorageAccount.DevelopmentStorageAccount,\n    tableName: \"Documents\",\n    partitionKey: p =\u003e p.Category,\n    rowKey: p =\u003e p.Id\n    serializer: [SERIALIZER]);\n\nawait docs.PutAsync(book);\n```\n\n\u003e If not provided, the serializer defaults to the `System.Text.Json`-based `DocumentSerializer.Default`.\n\nThe resulting differences in storage can be seen in the following screenshots of the \n[Azure Storage Explorer](https://azure.microsoft.com/en-us/features/storage-explorer/):\n\n![Screenshot of entity persisted with separate columns for properties](https://raw.githubusercontent.com/devlooped/TableStorage/main/assets/img/entity.png)\n\n![Screenshot of entity persisted as a document](https://raw.githubusercontent.com/devlooped/TableStorage/main/assets/img/document.png)\n\n\nThe `Type` column persisted in the documents table is the `Type.FullName` of the persisted entity, and the \n`Version` is the `[Major].[Minor]` of its assembly, which could be used for advanced data migration scenarios. \nThe major and minor version components are also provided as individual columns for easier querying \nby various version ranges, using `IDocumentRepository.EnumerateAsync(predicate)`.\n\n\u003c!-- #documents --\u003e\n\nIn addition to the default built-in JSON plain-text based serializer (which uses the \n[System.Text.Json](https://www.nuget.org/packages/system.text.json) package), there are other \nalternative serializers for the document-based repository, including various binary serializers \nwhich will instead persist the document as a byte array:\n\n[![Json.NET](https://img.shields.io/nuget/v/Devlooped.TableStorage.Newtonsoft.svg?color=royalblue\u0026label=Newtonsoft)](https://www.nuget.org/packages/Devlooped.TableStorage.Newtonsoft) [![Bson](https://img.shields.io/nuget/v/Devlooped.TableStorage.Bson.svg?color=royalblue\u0026label=Bson)](https://www.nuget.org/packages/Devlooped.TableStorage.Bson) [![MessagePack](https://img.shields.io/nuget/v/Devlooped.TableStorage.MessagePack.svg?color=royalblue\u0026label=MessagePack)](https://www.nuget.org/packages/Devlooped.TableStorage.MessagePack) [![Protobuf](https://img.shields.io/nuget/v/Devlooped.TableStorage.Protobuf.svg?color=royalblue\u0026label=Protobuf)](https://www.nuget.org/packages/Devlooped.TableStorage.Protobuf)\n\nYou can pass the serializer to use to the factory method as follows: \n\n```csharp\nvar repo = TableRepository.Create\u003cProduct\u003e(...,\n    serializer: [JsonDocumentSerializer|BsonDocumentSerializer|MessagePackDocumentSerializer|ProtobufDocumentSerializer].Default);\n```\n\n\u003e NOTE: when using alternative serializers, entities might need to be annotated with whatever \n\u003e attributes are required by the underlying libraries.\n\n\n## Attributes\n\nIf you want to avoid using strings with the factory methods, you can also annotate the \nentity type to modify the default values used:\n\n* `[Table(\"tableName\")]`: class-level attribute to change the default when no value is provided\n* `[PartitionKey]`: annotates the property that should be used as the partition key\n* `[RowKey]`: annotates the property that should be used as the row key.\n\nValues passed to the factory methods override declarative attributes.\n\nFor the products example above, your record entity could be:\n\n```csharp\n[Table(\"Products\")]\npublic record Product([PartitionKey] string Category, [RowKey] string Id) \n{\n  public string? Title { get; init; }\n  public double Price { get; init; }\n}\n```\n\nAnd creating the repository wouldn't require any arguments besides the storage account:\n\n```csharp\nvar repo = TableRepository.Create\u003cProduct\u003e(CloudStorageAccount.DevelopmentStorageAccount);\n```\n\nIn addition, if you want to omit a particular property from persistence, you can annotate \nit with `[Browsable(false)]` and it will be skipped when persisting and reading the entity.\n\n\n## TableEntity Support\n\nSince these repository APIs are quite a bit more intuitive than working directly against a  \n`TableClient`, you might want to retrieve/enumerate entities just by their built-in `TableEntity` \nproperties, like `PartitionKey`, `RowKey`, `Timestamp` and `ETag`. For this scenario, we \nalso support creating `ITableRepository\u003cTableEntity\u003e` and `ITablePartition\u003cTableEntity\u003e` \nby using the factory methods `TableRepository.Create(...)` and `TablePartition.Create(...)` \nwithout a (generic) entity type argument.\n\nFor example, given you know all `Region` entities saved in the example above, use the region `Code` \nas the `RowKey`, you could simply enumerate all regions without using the `Region` type at all:\n\n```csharp\nvar account = CloudStorageAccount.DevelopmentStorageAccount; // or production one\nvar repo = TablePartition.Create(storageAccount, \n  tableName: \"Reference\",\n  partitionKey: \"Region\");\n\n// Enumerate all regions within the partition as plain TableEntities\nawait foreach (TableEntity region in repo.EnumerateAsync())\n   Console.WriteLine(region.RowKey);\n```\n\nYou can access and add additional properties by just using the entity indexer, which you can \nlater persist by calling `PutAsync`:\n\n```csharp\nawait repo.PutAsync(\n    new TableEntity(\"Book\", \"9781473217386\") \n    {\n        [\"Title\"] = \"Neuromancer\",\n        [\"Price\"] = 7.32\n    });\n\nvar entity = await repo.GetAsync(\"Book\", \"9781473217386\");\n\nAssert.Equal(\"Neuromancer\", entity[\"Title\"]);\nAssert.Equal(7.32, (double)entity[\"Price\"]);\n```\n\n\u003c!-- #content --\u003e\n\n## Installation\n\n```\n\u003e Install-Package Devlooped.TableStorage\n```\n\nAll packages also come in source-only versions, if you want to avoid an additional assembly dependency:\n\n```\n\u003e Install-Package Devlooped.TableStorage.Source\n```\n\nThe source-only packages includes all types with the default visibility (internal), so you can decide \nwhat types to make public by declaring a partial class with the desired visibility. To make them all \npublic, for example, you can include the same [Visibility.cs](https://github.com/devlooped/TableStorage/blob/main/src/TableStorage/Visibility.cs) \nthat the compiled version uses.\n\n\n## Dogfooding\n\n[![CI Version](https://img.shields.io/endpoint?url=https://shields.kzu.io/vpre/Devlooped.TableStorage/main\u0026label=nuget.ci\u0026color=brightgreen)](https://pkg.kzu.io/index.json)\n[![Build](https://github.com/devlooped/TableStorage/workflows/build/badge.svg?branch=main)](https://github.com/devlooped/TableStorage/actions)\n\nWe also produce CI packages from branches and pull requests so you can dogfood builds as quickly as they are produced. \n\nThe CI feed is `https://pkg.kzu.io/index.json`. \n\nThe versioning scheme for packages is:\n\n- PR builds: *42.42.42-pr*`[NUMBER]`\n- Branch builds: *42.42.42-*`[BRANCH]`.`[COMMITS]`\n\n\u003c!-- #sponsors --\u003e\n\u003c!-- include https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n# Sponsors \n\n\u003c!-- sponsors.md --\u003e\n[![Clarius Org](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/clarius.png \"Clarius Org\")](https://github.com/clarius)\n[![Kirill Osenkov](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KirillOsenkov.png \"Kirill Osenkov\")](https://github.com/KirillOsenkov)\n[![MFB Technologies, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/MFB-Technologies-Inc.png \"MFB Technologies, Inc.\")](https://github.com/MFB-Technologies-Inc)\n[![Torutek](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/torutek-gh.png \"Torutek\")](https://github.com/torutek-gh)\n[![DRIVE.NET, Inc.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/drivenet.png \"DRIVE.NET, Inc.\")](https://github.com/drivenet)\n[![Keith Pickford](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Keflon.png \"Keith Pickford\")](https://github.com/Keflon)\n[![Thomas Bolon](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/tbolon.png \"Thomas Bolon\")](https://github.com/tbolon)\n[![Kori Francis](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/kfrancis.png \"Kori Francis\")](https://github.com/kfrancis)\n[![Toni Wenzel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/twenzel.png \"Toni Wenzel\")](https://github.com/twenzel)\n[![Uno Platform](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/unoplatform.png \"Uno Platform\")](https://github.com/unoplatform)\n[![Dan Siegel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/dansiegel.png \"Dan Siegel\")](https://github.com/dansiegel)\n[![Reuben Swartz](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/rbnswartz.png \"Reuben Swartz\")](https://github.com/rbnswartz)\n[![Jacob Foshee](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jfoshee.png \"Jacob Foshee\")](https://github.com/jfoshee)\n[![](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Mrxx99.png \"\")](https://github.com/Mrxx99)\n[![Eric Johnson](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/eajhnsn1.png \"Eric Johnson\")](https://github.com/eajhnsn1)\n[![Ix Technologies B.V.](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/IxTechnologies.png \"Ix Technologies B.V.\")](https://github.com/IxTechnologies)\n[![David JENNI](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/davidjenni.png \"David JENNI\")](https://github.com/davidjenni)\n[![Jonathan ](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/Jonathan-Hickey.png \"Jonathan \")](https://github.com/Jonathan-Hickey)\n[![Charley Wu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/akunzai.png \"Charley Wu\")](https://github.com/akunzai)\n[![Jakob Tikjøb Andersen](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/jakobt.png \"Jakob Tikjøb Andersen\")](https://github.com/jakobt)\n[![Seann Alexander](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/seanalexander.png \"Seann Alexander\")](https://github.com/seanalexander)\n[![Tino Hager](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/tinohager.png \"Tino Hager\")](https://github.com/tinohager)\n[![Mark Seemann](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/ploeh.png \"Mark Seemann\")](https://github.com/ploeh)\n[![Ken Bonny](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/KenBonny.png \"Ken Bonny\")](https://github.com/KenBonny)\n[![Simon Cropp](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/SimonCropp.png \"Simon Cropp\")](https://github.com/SimonCropp)\n[![agileworks-eu](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/agileworks-eu.png \"agileworks-eu\")](https://github.com/agileworks-eu)\n[![sorahex](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/sorahex.png \"sorahex\")](https://github.com/sorahex)\n[![Zheyu Shen](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/arsdragonfly.png \"Zheyu Shen\")](https://github.com/arsdragonfly)\n[![Vezel](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/vezel-dev.png \"Vezel\")](https://github.com/vezel-dev)\n[![ChilliCream](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/ChilliCream.png \"ChilliCream\")](https://github.com/ChilliCream)\n[![4OTC](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/4OTC.png \"4OTC\")](https://github.com/4OTC)\n[![Vincent Limo](https://raw.githubusercontent.com/devlooped/sponsors/main/.github/avatars/v-limo.png \"Vincent Limo\")](https://github.com/v-limo)\n\n\n\u003c!-- sponsors.md --\u003e\n\n[![Sponsor this project](https://raw.githubusercontent.com/devlooped/sponsors/main/sponsor.png \"Sponsor this project\")](https://github.com/sponsors/devlooped)\n\u0026nbsp;\n\n[Learn more about GitHub Sponsors](https://github.com/sponsors)\n\n\u003c!-- https://github.com/devlooped/sponsors/raw/main/footer.md --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Ftablestorage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevlooped%2Ftablestorage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevlooped%2Ftablestorage/lists"}