{"id":20875007,"url":"https://github.com/servicestack/pocodynamo","last_synced_at":"2025-05-12T15:30:53.254Z","repository":{"id":35701210,"uuid":"39978563","full_name":"ServiceStack/PocoDynamo","owner":"ServiceStack","description":"C# .NET Typed POCO Client for AWS Dynamo DB","archived":false,"fork":false,"pushed_at":"2020-10-17T02:59:00.000Z","size":55,"stargazers_count":40,"open_issues_count":0,"forks_count":8,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-08T03:37:03.582Z","etag":null,"topics":["aws","c-sharp","dynamodb","high-performance","linq","mono","net-core","net-framework","poco"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ServiceStack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-31T00:32:14.000Z","updated_at":"2023-11-16T18:00:48.000Z","dependencies_parsed_at":"2022-08-26T10:22:33.567Z","dependency_job_id":null,"html_url":"https://github.com/ServiceStack/PocoDynamo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2FPocoDynamo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2FPocoDynamo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2FPocoDynamo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ServiceStack%2FPocoDynamo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ServiceStack","download_url":"https://codeload.github.com/ServiceStack/PocoDynamo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253765782,"owners_count":21960788,"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":["aws","c-sharp","dynamodb","high-performance","linq","mono","net-core","net-framework","poco"],"created_at":"2024-11-18T06:40:56.553Z","updated_at":"2025-05-12T15:30:52.980Z","avatar_url":"https://github.com/ServiceStack.png","language":null,"readme":"Follow [@ServiceStack](https://twitter.com/servicestack) for updates, or [StackOverflow](http://stackoverflow.com/questions/ask) or the [Customer Forums](https://forums.servicestack.net/) for support.\n\n# PocoDynamo\n\nis a highly productive, feature-rich, typed .NET client which extends \n[ServiceStack's Simple POCO life](http://stackoverflow.com/a/32940275/85785) \nby enabling re-use of your code-first data models with Amazon's industrial strength and highly-scalable \nNoSQL [DynamoDB](https://aws.amazon.com/dynamodb/).\n\n#### First class support for reusable, code-first POCOs\n\nPocoDynamo is conceptually similar to ServiceStack's other code-first\n[OrmLite](https://github.com/ServiceStack/ServiceStack.OrmLite) and \n[Redis](https://github.com/ServiceStack/ServiceStack.Redis) clients by providing a high-fidelity, managed client that enhances\nAWSSDK's low-level [IAmazonDynamoDB client](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/UsingAWSsdkForDotNet.html), \nwith rich, native support for intuitively mapping your re-usable code-first POCO Data models into \n[DynamoDB Data Types](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Types.html). \n\n![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/related-customer.png)\n\n#### [AutoQuery DynamoDB](https://github.com/ServiceStack/ServiceStack/wiki/AutoQuery-DynamoDB)\n\nBuilt on top of PocoDynamo, [AutoQuery Data's](https://github.com/ServiceStack/ServiceStack/wiki/AutoQuery-Data) \n`DynamoDbSource` provides the most productive development experience for effortlessly creating rich, queryable \nand optimized Services for DynamoDB data stores using only a typed Request DTO.\n\n### Quick Preview\n\nA quick CRUD preview of `PocoDynaamo` feature-rich high-level Typed client:\n\n```csharp\nusing System;\nusing Amazon;\nusing Amazon.DynamoDBv2;\nusing ServiceStack;\nusing ServiceStack.Text;\nusing ServiceStack.Aws.DynamoDb;\nusing ServiceStack.DataAnnotations;\n\nvar awsDb = new AmazonDynamoDBClient(\"keyId\",\"key\",\n    new AmazonDynamoDBConfig { ServiceURL=\"http://localhost:8000\"});\nvar db = new PocoDynamo(awsDb);\n\npublic class Todo\n{\n    [AutoIncrement]\n    public long Id { get; set; }\n    public string Content { get; set; }\n    public int Order { get; set; }\n    public bool Done { get; set; }\n}\n\ndb.RegisterTable\u003cTodo\u003e();\n\ndb.DeleteTable\u003cTodo\u003e();  // Delete existing Todo Table (if any)\ndb.InitSchema();         // Creates Todo DynamoDB Table\n\nvar newTodo = new Todo {\n    Content = \"Learn PocoDynamo\",\n    Order = 1\n};\ndb.PutItem(newTodo);\n\nvar savedTodo = db.GetItem\u003cTodo\u003e(newTodo.Id);\n\"Saved Todo: {0}\".Print(savedTodo.Dump());\n\nsavedTodo.Done = true;\ndb.PutItem(savedTodo);\n\nvar updatedTodo = db.GetItem\u003cTodo\u003e(newTodo.Id);\n\"Updated Todo: {0}\".Print(updatedTodo.Dump());\n\ndb.DeleteItem\u003cTodo\u003e(newTodo.Id);\n\nvar remainingTodos = db.GetAll\u003cTodo\u003e();\n\"No more Todos: {0}\".Print(remainingTodos.Dump());\n```\n\n## Features\n\n#### Advanced idiomatic .NET client\n\nPocoDynamo provides an idiomatic API that leverages .NET advanced language features with streaming API's returning\n`IEnumerable\u003cT\u003e` lazily evaluated responses that transparently performs multi-paged requests \nbehind-the-scenes whilst the resultset is iterated. It high-level API's provides a clean lightweight adapter to\ntransparently map between .NET built-in data types and DynamoDB's low-level attribute values. Its efficient batched \nAPI's take advantage of DynamoDB's `BatchWriteItem` and `BatchGetItem` batch operations to perform the minimum number \nof requests required to implement each API.\n\n#### Typed, LINQ provider for Query and Scan Operations\n\nPocoDynamo also provides rich, typed LINQ-like querying support for constructing DynamoDB Query and Scan operations, \ndramatically reducing the effort to query DynamoDB, enhancing readability whilst benefiting from Type safety in .NET. \n\n#### Declarative Tables and Indexes\n\nBehind the scenes DynamoDB is built on a dynamic schema which whilst open and flexible, can be cumbersome to work with \ndirectly in typed languages like C#. PocoDynamo bridges the gap and lets your app bind to impl-free and declarative POCO \ndata models that provide an ideal high-level abstraction for your business logic, hiding a lot of the complexity of \nworking with DynamoDB - dramatically reducing the code and effort required whilst increasing the readability and \nmaintainability of your Apps business logic.\n\nIt includes optimal support for defining simple local indexes which only require declaratively annotating properties \nto index with an `[Index]` attribute.\n\nTyped POCO Data Models can be used to define more complex Local and Global DynamoDB Indexes by implementing \n`IGlobalIndex\u003cPoco\u003e` or `ILocalIndex\u003cPoco\u003e` interfaces which PocoDynamo uses along with the POCOs class structure \nto construct Table indexes at the same time it creates the tables.\n\nIn this way the Type is used as a DSL to define DynamoDB indexes where the definition of the index is decoupled from \nthe imperative code required to create and query it, reducing the effort to create them whilst improving the \nvisualization and understanding of your DynamoDB architecture which can be inferred at a glance from the POCO's \nType definition. PocoDynamo also includes first-class support for constructing and querying Global and Local Indexes \nusing a familiar, typed LINQ provider.\n\n#### Resilient\n\nEach operation is called within a managed execution which transparently absorbs the variance in cloud services \nreliability with automatic retries of temporary errors, using an exponential backoff as recommended by Amazon. \n\n#### Enhances existing APIs\n\nPocoDynamo API's are a lightweight layer modeled after DynamoDB API's making it predictable the DynamoDB operations \neach API calls under the hood, retaining your existing knowledge investment in DynamoDB. \nWhen more flexibility is needed you can access the low-level `AmazonDynamoDBclient from the `IPocoDynamo.DynamoDb` \nproperty and talk with it directly.\n\nWhilst PocoDynamo doesn't save you for needing to learn DynamoDB, its deep integration with .NET and rich support for \nPOCO's smoothes out the impedance mismatches to enable an type-safe, idiomatic, productive development experience.\n\n#### High-level features\n\nPocoDynamo includes its own high-level features to improve the re-usability of your POCO models and the development \nexperience of working with DynamoDB with support for Auto Incrementing sequences, Query expression builders, \nauto escaping and converting of Reserved Words to placeholder values, configurable converters, scoped client \nconfigurations, related items, conventions, aliases, dep-free data annotation attributes and more.\n\n## Download\n\nPocoDynamo is contained in ServiceStack's AWS NuGet package:\n\n    PM\u003e Install-Package ServiceStack.Aws\n   \n\u003csub\u003ePocoDynamo has a 10 Tables [free-quota usage](https://servicestack.net/download#free-quotas) limit which can be unlocked with a [commercial license key](https://servicestack.net/pricing).\u003c/sub\u003e\n    \nTo get started we'll need to create an instance of `AmazonDynamoDBClient` with your AWS credentials and Region info:\n\n```csharp\nvar awsDb = new AmazonDynamoDBClient(AWS_ACCESS_KEY, AWS_SECRET_KEY, RegionEndpoint.USEast1);\n```\n\nThen to create a PocoDynamo client pass the configured AmazonDynamoDBClient instance above:\n\n```csharp\nvar db = new PocoDynamo(awsDb);\n```\n\n\u003e Clients are Thread-Safe so you can register them as a singleton and share the same instance throughout your App\n\n### [Source Code](https://github.com/ServiceStack/ServiceStack.Aws/tree/master/src/ServiceStack.Aws/DynamoDb)\n\nThe Source Code for PocoDynamo is maintained in [ServiceStack.Aws](https://github.com/ServiceStack/ServiceStack.Aws/) repository.\n\n### Download Local DynamoDB\n\nIt's recommended to download [local DynamoDB](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Tools.DynamoDBLocal.html#Tools.DynamoDBLocal.DownloadingAndRunning)\nas it lets you develop against a local DynamoDB instance, saving you needing a network connection or AWS account.\n\nYou can connect to your local DynamoDB instance by configuring the `AmazonDynamoDBClient` to point to the default url\nwhere Local DynamoDB instance is running:\n\n```csharp\nvar awsDb = new AmazonDynamoDBClient(\"keyId\", \"key\", new AmazonDynamoDBConfig {\n    ServiceURL = \"http://localhost:8000\",\n});\n\nvar db = new PocoDynamo(awsDb);\n```\n\nWe've found the latest version of Local DynamoDB to be a robust and fast substitute for AWS, that eliminates waiting \ntimes for things like creating and dropping tables whilst only slightly deviating from the capabilities of AWS where \nit doesn't always include the additional limitations imposed when hosted on AWS.\n\n## Usage\n\nTo illustrate how PocoDynamo simplifies working with DynamoDB, we'll walk-through creating and retrieving the Simple \n[Todo model](https://github.com/ServiceStackApps/AwsApps/blob/04dea6472fd73ea2e55f1aa748fff6e8784b339c/src/AwsApps/todo/TodoService.cs#L9)\nused in the [DynamoDB-powered AWS Todo Example](http://awsapps.servicestack.net/todo/) and compare it against the code\nrequired when using AWSSDK's `IAmazonDynamoDB` client directly.\n\nThe simple `Todo` POCO is the same data model used to store TODO's in every major RDBMS's with \n[OrmLite](https://github.com/ServiceStack/ServiceStack.OrmLite), in Redis with \n[ServiceStack.Redis](https://github.com/ServiceStack/ServiceStack.Redis) as well as \nevery supported [Caching provider](https://github.com/ServiceStack/ServiceStack/wiki/Caching). \n\nPocoDynamo increases the re-use of `Todo` again which can now be used to store TODO's in DynamoDB as well: \n\n```csharp\npublic class Todo\n{\n    [AutoIncrement]\n    public long Id { get; set; }\n    public string Content { get; set; }\n    public int Order { get; set; }\n    public bool Done { get; set; }\n}\n```\n\n### Creating a Table with PocoDynamo\n\nPocoDynamo enables a declarative code-first approach where it's able to create DynamoDB Table schemas from just your \nPOCO class definition. Whilst you could call `db.CreateTable\u003cTodo\u003e()` API and create the Table directly, the recommended \napproach is instead to register all the tables your App uses with PocoDynamo on Startup, then just call `InitSchema()` \nwhich will go through and create all missing tables:\n\n```csharp\n//PocoDynamo\nvar db = new PocoDynamo(awsDb)\n    .RegisterTable\u003cTodo\u003e();\n\ndb.InitSchema();\n\ndb.GetTableNames().PrintDump();\n```\n\nIn this way your App ends up in the same state with all tables created if it was started with **no tables**, **all tables** \nor only a **partial list** of tables. After the tables are created we query DynamoDB to dump its entire list of Tables, \nwhich if you started with an empty DynamoDB instance would print the single **Todo** table name to the Console:\n\n    [\n        Todo\n    ]\n    \n### Complete PocoDynamo TODO example\n\nBefore going through the details of how it all works under-the-hood, here's a quick overview of what it looks likes to \nuse PocoDynamo for developing a simple CRUD App. The ServiceStack \n[TodoService](https://github.com/ServiceStackApps/AwsApps/blob/master/src/AwsApps/todo/TodoService.cs) \nbelow contains the full server implementation required to implement the REST API to power \n[Backbone's famous TODO App](http://todomvc.com/examples/backbone/), rewritten to store all TODO items in DynamoDB:\n\n```csharp\n//PocoDynamo\npublic class TodoService : Service\n{\n    public IPocoDynamo Dynamo { get; set; }\n\n    public object Get(Todo todo)\n    {\n        if (todo.Id != default(long))\n            return Dynamo.GetItem\u003cTodo\u003e(todo.Id);\n\n        return Dynamo.GetAll\u003cTodo\u003e();\n    }\n\n    public Todo Post(Todo todo)\n    {\n        Dynamo.PutItem(todo);\n        return todo;\n    }\n\n    public Todo Put(Todo todo)\n    {\n        return Post(todo);\n    }\n\n    public void Delete(Todo todo)\n    {\n        Dynamo.DeleteItem\u003cTodo\u003e(todo.Id);\n    }\n}\n```\n\nWe can see `IPocoDynamo` is just a normal IOC dependency that provides high-level API's that work directly with POCO's \nand built-in .NET data types, enabling the minimum effort to store, get and delete data from DynamoDB.\n\n### Creating a DynamoDB Table using AmazonDynamoDBClient\n\nThe equivalent imperative code to create the Todo DynamoDB table above would require creating executing the \n`CreateTableRequest` below:\n\n```csharp\n//AWSSDK\nvar request = new CreateTableRequest\n{\n    TableName = \"Todo\",\n    KeySchema = new List\u003cKeySchemaElement\u003e\n    {\n        new KeySchemaElement(\"Id\", KeyType.HASH),\n    },\n    AttributeDefinitions = new List\u003cAttributeDefinition\u003e\n    {\n        new AttributeDefinition(\"Id\", ScalarAttributeType.N),\n    },\n    ProvisionedThroughput = new ProvisionedThroughput\n    {\n        ReadCapacityUnits = 10,\n        WriteCapacityUnits = 5,\n    }\n};\nawsDb.CreateTable(request);\n```\n\nDynamoDB Tables take a little while to create in AWS so we can't use it immediately, instead you'll need to \nperiodically poll to check the status for when it's ready:\n\n```csharp\n//AWSSDK\nvar startAt = DateTime.UtcNow;\nvar timeout = TimeSpan.FromSeconds(60);\ndo\n{\n    try\n    {\n        var descResponse = awsDb.DescribeTable(\"Todo\");\n        if (descResponse.Table.TableStatus == DynamoStatus.Active)\n            break;\n\n        Thread.Sleep(TimeSpan.FromSeconds(2));\n    }\n    catch (ResourceNotFoundException)\n    {\n        // DescribeTable is eventually consistent. So you might get resource not found.\n    }\n\n    if (DateTime.UtcNow - startAt \u003e timeout)\n        throw new TimeoutException(\"Exceeded timeout of {0}\".Fmt(timeout));\n\n} while (true);\n```\n\nOnce the table is Active we can start using it, to get the list of table names we send a `ListTablesRequest`:\n\n```csharp\n//AWSSDK\nvar listResponse = awsDb.ListTables(new ListTablesRequest());\nvar tableNames = listResponse.TableNames;\ntableNames.PrintDump();\n```\n\n## Managed DynamoDB Client\n\nAs we can see using the `AmazonDynamoDBClient` directly requires a lot more imperative code, but it also ends up doing \na lot less. We've not included the logic to query existing tables so only the missing tables are created, we've not \nimplemented any error handling or Retry logic (important for Cloud Services) and we're not checking to make sure we've \ncollected the entire list of results (implementing paging when necessary).\n\nWhereas every request in PocoDynamo is invoked inside a managed execution where any temporary errors are retried using the \n[AWS recommended retries exponential backoff](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ErrorHandling.html#APIRetries).\n\nAll PocoDynamo API's returning `IEnumerable\u003cT\u003e` returns a lazy evaluated stream which behind-the-scenes sends multiple\npaged requests as needed whilst the sequence is being iterated. As LINQ API's are also lazily evaluated you could use \n`Take()` to only download the exact number results you need. So you can query the first 100 table names with:\n\n```csharp\n//PocoDynamo\nvar first100TableNames = db.GetTableNames().Take(100).ToList();\n```\n\nand PocoDynamo will only make the minimum number of requests required to fetch the first 100 results.\n\n## AutoIncrement Primary Keys\n\nOnce the `Todo` table is created we can start adding TODOs to it. If we were using OrmLite. the `[AutoIncrement]` \nattribute lets us use the RDBMS's native support for auto incrementing sequences to populate the Id primary key. \nUnfortunately DynamoDB lacks an auto increment feature and instead recommends the user to supply a unique key as shown\nin their [DynamoDB Forum example](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DataModel.html)\nwhere they've chosen a Forum Name as the Hash Key of the Forum and Thread tables, whilst the Reply comment uses a\nconcatenation of `ForumName` + `#` + `ThreadSubject` as its Hash Key and the `ReplyDateTime` for the Range Key. \n\nHowever auto incrementing Ids have a number of useful properties making it ideal for identifying data:\n\n  - **Unique** - Each new item is guaranteed to have a unique Id that's higher than all Ids before it\n  - **Sequential** - A useful property to ensure consistent results when paging or ordering\n  - **Never change** - To ensure a constant key that never changes, Ids shouldn't contain data it references\n  - **Easy to read** - Humans have a better chance to read and remember a number than a concatenated string\n  - **Easy to reference** - It's easier to reference a predictable numeric field than a concatenated string\n\nThey're also more re-usable as most data stores have native support for integer primary keys. For these reasons we've\nadded support for Auto-Incrementing integer primary keys in PocoDynamo where Ids annotated with `[AutoIncrement]` \nattribute are automatically populated with the next id in its sequence.\n\n#### ISequenceSource\n\nThe Auto Incrementing functionality is provided by the\n[ISequenceSource](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/ISequenceSource.cs) \ninterface:\n\n```csharp\npublic interface ISequenceSource : IRequiresSchema\n{\n    long Increment(string key, int amount = 1);\n    void Reset(string key, int startingAt = 0);\n}\n```\n#### DynamoDbSequenceGenerator\n\nThe default implementation uses \n[DynamoDbSequenceGenerator](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/DynamoDbSequenceGenerator.cs)\nwhich stores sequences for each table in the `Seq` DynamoDB Table so no additional services are required. To ensure\nunique incrementing sequences in DynamoDB, PocoDynamo uses UpdateItemRequest's `AttributeValueUpdate` feature to perform\natomic value updates. PocoDynamo sequences are also very efficient and only require a single DynamoDB call to populate a \nbatch of Primary Key Ids which are also guaranteed to be in order (and without gaps) for batches that are stored together. \n\n#### RedisSequenceSource\n\nIf preferred you can instead instruct PocoDynamo to maintain sequences in Redis using \n[RedisSequenceSource](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Server/RedisSequenceSource.cs)\nor alternatively inject your own implementation which can be configured in PocoDynamo with:\n\n```csharp\nvar db = new PocoDynamo(awsDb) {\n    Sequences = new RedisSequenceSource(redisManager),\n};\n```\n\n## Putting items with PocoDynamo\n\nAs we can take advantage of Auto Incrementing Id's, storing Items becomes as simple as creating a number of POCO's and\ncalling PutItems:\n\n```csharp\n//PocoDynamo\nvar todos = 100.Times(i =\u003e new Todo { Content = \"TODO \" + i, Order = i });\ndb.PutItems(todos);\n```\n\n## Putting items with AmazonDynamoDBClient\n\nTo do this manually with `AmazonDynamoDBClient` you'd need to create and `UpdateItemRequest` to update the counter\nmaintaining your TODO sequences:\n\n```csharp\n//AWSSDK\nvar incrRequest = new UpdateItemRequest\n{\n    TableName = \"Seq\",\n    Key = new Dictionary\u003cstring, AttributeValue\u003e {\n        {\"Id\", new AttributeValue { S = \"Todo\" } }\n    },\n    AttributeUpdates = new Dictionary\u003cstring, AttributeValueUpdate\u003e {\n        {\n            \"Counter\",\n            new AttributeValueUpdate {\n                Action = AttributeAction.ADD,\n                Value = new AttributeValue { N = \"100\" }\n            }\n        }\n    },\n    ReturnValues = ReturnValue.ALL_NEW,\n};\n\nvar response = awsDb.UpdateItem(incrRequest);\nvar nextSequences = Convert.ToInt64(response.Attributes[\"Counter\"].N);\n```\n\nAfter you know which sequence to start with you can start putting items using a Dictionary of Attribute Values:\n\n```csharp\n//AWSSDK\nfor (int i = 0; i \u003c 100; i++)\n{\n    var putRequest = new PutItemRequest(\"Todo\",\n        new Dictionary\u003cstring, AttributeValue\u003e {\n            { \"Id\", new AttributeValue { N = (nextSequences - 100 + i).ToString() } },\n            { \"Content\", new AttributeValue(\"TODO \" + i) },\n            { \"Order\", new AttributeValue { N = i.ToString() } },\n            { \"Done\", new AttributeValue { BOOL = false } },\n        });\n\n    awsDb.PutItem(putRequest);\n}\n```\n\nAlthough even without the managed execution this still isn't equivalent to PocoDynamo's example above as to store \nmultiple items efficiently PocoDynamo `PutItems()` API batches multiple Items in 4x `BatchWriteItemRequest` \nbehind-the-scenes, the minimum number needed due to DynamoDB's maximum Write Batch size limit of 25 requests.\n\n## Getting Items with PocoDynamo\n\nGetting an item just requires the Generic Type and the primary key of the item to fetch:\n\n```csharp\nvar todo = db.GetItem\u003cTodo\u003e(1);\ntodo.PrintDump();\n```\n\nWhich returns the Todo item if it exists, or `null` if it doesn't.\n\nFetching all table items is where an understanding of DynamoDB's architecture and its limits become important. DynamoDB \nachieves its scalability by partitioning your data across multiple partitions based on its hash Key (aka Primary Key). \nThis means that the only way to efficiently query across data containing multiple primary keys is to either explicitly \ncreate a [Global Secondary Index](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html) or perform a \nfull-table Scan. \n\nHowever table scans in DynamoDB are more inefficient than full table scans in RDBMS's since it has to scan across\nmultiple partitions which can quickly use up your table's provisioned throughput, as such scans should be limited \nto low usage areas. \n\nWith that said, you can do Table Scans in PocoDynamo using API's starting with `Scan*` prefix, e.g. to return\nall Todo items:\n\n```csharp\n//PocoDynamo\nIEnumerable\u003cTodo\u003e todos = db.ScanAll\u003cTodo\u003e();\n```\n\nAs IEnumerable's are lazily executed, it only starts sending `ScanRequest` to fetch all Items once the IEnumerable is \niterated, which it does in **batches of 1000** (configurable with `PocoDynamo.PagingLimit`). \n\nTo fetch all items you can just call `ToList()`:\n\n```csharp\nvar allTodos = todos.ToList();\nallTodos.PrintDump();\n```\n\nWhich incidentally is also just what `db.GetAll\u003cTodo\u003e()` does. \n\n## Getting Items with AWSSDK\n\nTo fetch the same single item with the AWSSDK client you'd construct and send a `GetItemRequest`, e.g:\n\n```csharp\n//AWSSDK\nvar request = new GetItemRequest\n{\n    TableName = \"Todo\",\n    Key = new Dictionary\u003cstring, AttributeValue\u003e {\n        { \"Id\", new AttributeValue { N = \"1\"} }\n    },\n    ConsistentRead = true,\n};\n\nvar response = awsDb.GetItem(request);\nvar todo = new Todo\n{\n    Id = Convert.ToInt64(response.Item[\"Id\"].N),\n    Content = response.Item[\"Content\"].S,\n    Order = Convert.ToInt32(response.Item[\"Order\"].N),\n    Done = response.Item[\"Done\"].BOOL,\n};\n```\n\nAlthough this is a little fragile as it doesn't handle the case when attributes (aka Properties) or the item doesn't exist.\n\nDoing a full-table scan is pretty straight-forward although as you're scanning the entire table you'll want to implement\nthe paging to scan through all items, which looks like:\n\n```csharp\n//AWSSDK\nvar request = new ScanRequest\n{\n    TableName = \"Todo\",\n    Limit = 1000,\n};\n\nvar allTodos = new List\u003cTodo\u003e();\nScanResponse response = null;\ndo\n{\n    if (response != null)\n        request.ExclusiveStartKey = response.LastEvaluatedKey;\n\n    response = awsDb.Scan(request);\n\n    foreach (var item in response.Items)\n    {\n        var todo = new Todo\n        {\n            Id = Convert.ToInt64(item[\"Id\"].N),\n            Content = item[\"Content\"].S,\n            Order = Convert.ToInt32(item[\"Order\"].N),\n            Done = item[\"Done\"].BOOL,\n        };\n        allTodos.Add(todo);\n    }\n    \n} while (response.LastEvaluatedKey != null \u0026\u0026 response.LastEvaluatedKey.Count \u003e 0);\n\nallTodos.PrintDump();\n```\n\n## Deleting an Item with PocoDynamo\n\nDeleting an item is similar to getting an item which just needs the generic type and primary key:\n\n```csharp\n//PocoDynamo\ndb.DeleteItem\u003cTodo\u003e(1);\n```\n\n## Deleting an Item with AWSSDK\n\nWhich just sends a `DeleteItemRequest` to delete the Item:\n\n```csharp\n//AWSSDK\nvar request = new DeleteItemRequest\n{\n    TableName = \"Todo\",\n    Key = new Dictionary\u003cstring, AttributeValue\u003e {\n        { \"Id\", new AttributeValue { N = \"1\"} }\n    },\n};\n\nawsDb.DeleteItem(request);\n```\n\n## Updating an Item with PocoDynamo\n\nThe simplest usage is to pass in a partially populated POCO where any Hash or Range Keys are added to the \nKey Condition and any non-default values are replaced. E.g the query below updates the Customer's Age to **42**:\n\n```csharp\ndb.UpdateItemNonDefaults(new Customer { Id = customer.Id, Age = 42 });\n```\n\nDynamoDB's UpdateItem supports 3 different operation types: \n\n - `PUT` to replace an Attribute Value\n - `ADD` to add to an existing Attribute Value\n - `DELETE` to delete the specified Attributes\n\nExamples of all 3 are contained in the examples below which changes the Customer's `Nationality` to \n**Australian**, reduces their `Age` by **1** and deletes their `Name` and `Orders`:\n\n```csharp\ndb.UpdateItem(customer.Id, \n    put: () =\u003e new Customer {\n        Nationality = \"Australian\"\n    },\n    add: () =\u003e new Customer {\n        Age = -1\n    },\n    delete: x =\u003e new { x.Name, x.Orders });\n```\n\nThe same Typed API above is also available in the more flexible and untyped form below:\n\n```csharp\ndb.UpdateItem\u003cCustomer\u003e(new DynamoUpdateItem\n{\n    Hash = customer.Id,\n    Put = new Dictionary\u003cstring, object\u003e\n    {\n        { \"Nationality\", \"Australian\" },\n    },\n    Add = new Dictionary\u003cstring, object\u003e\n    {\n        { \"Age\", -1 }\n    },\n    Delete = new[] { \"Name\", \"Orders\" },\n});\n```\n\n### Update with Conditional Expressions\n\nPocoDynamo also has Typed API support for \n[DynamoDB Conitional Expressions](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html#API_PutItem_RequestSyntax)\nby using the `Condition()` API, e.g:\n\n```csharp\nvar q = db.UpdateExpression\u003cCustomer\u003e(customer.Id)\n    .Set(() =\u003e new Customer { Nationality = \"Australian\" })\n    .Add(() =\u003e new Customer { Age = decrBy })\n    .Remove(x =\u003e new { x.Name, x.Orders })\n    .Condition(x =\u003e x.Age == 27);\n\nvar succeeded = db.UpdateItem(q);\n```\n\n## Querying\n\nThe simple Todo example should give you a feel for using PocoDynamo to handle basic CRUD operations. \nAnother area where PocoDynamo adds a lot of value which can be fairly cumbersome to do without, is in creating\n[Query and Scan](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/QueryAndScan.html) \nrequests to query data in DynamoDB Tables.\n\n### QueryExpressions are QueryRequests\n\nThe query functionality in PocoDynamo is available on the `QueryExpression\u003cT\u003e` class which is used as a typed query \nbuilder to construct your Query request. An important attribute about QueryExpression's are that they inherit AWSSDK's \n[QueryRequest](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LowLevelDotNetQuerying.html) Request DTO. \n\nThis provides a number of benefits, they're easy to use and highly introspectable since each API just populates \ndifferent fields in the Request DTO. They're also highly reusable as QueryExpressions can be executed as-is in AWSSDK \nDynamoDB client and vice-versa with PocoDynamo's `Query` API's executing both `QueryExpression\u003cT\u003e` and `QueryRequest` \nDTOs. The difference with PocoDynamo's Query API is that they provide managed exeuction, lazy evaluation, paged queries \nand auto-conversion of dynamic results into typed POCOs.\n\n### Query Usage\n\n[DynamoDB Query's](http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) enable efficient \nquerying of data in DynamoDB as it's limited to querying the indexed Hash and Range Keys on your Tables or Table Indexes. \nAlthough it has the major limitation that it always needs to specify a Hash condition, essentially forcing the query \nto be scoped to a single partition. This makes it fairly useless for Tables with only a single Hash Primary Key like \n`Todo` as the query condition will always limit to a maximum of 1 result.\n\nNevertheless we can still use it to show how to perform server-side queries with PocoDynamo. To create a \nQueryExpression use the `FromQuery*` API's. It accepts a `KeyConditionExpression` as the first argument given it's a \nmandatory requirement for Query Requests which uses it to identify the partition the query should be executed on:\n\n```csharp\nvar q = db.FromQuery\u003cTodo\u003e(x =\u003e x.Id == 1);\n```\n\n#### Key Condition and Filter Expressions\n\nPocoDynamo parses this lambda expression to return a populated `QueryExpression\u003cTodo\u003e` which you can inspect to find \nthe `TableName` set to **Todo** and the `KeyConditionExpression` set to **(Id = :k0)** with the \n`ExpressionAttributeValues` Dictionary containing a Numeric value of **1** for the key **:k0**.\n\nFrom here you can continue constructing the QueryRequest DTO by populating its properties directly or by calling \n`QueryExpression` high-level methods (modeled after the properties they populate), e.g. the `KeyCondition()` method\npopulates the `KeyConditionExpression` property, `Filter()` populates the `FilterExpression` property and any arguments \nused in any expression are automatically parameterized and added to the `ExpressionAttributeValues` collection:\n\n```csharp\nvar q = db.FromQuery\u003cTodo\u003e()\n    .KeyCondition(x =\u003e x.Id == 1) //Equivalent to: db.FromQuery\u003cTodo\u003e(x =\u003e x.Id == 1)\n    .Filter(x =\u003e x.Done);\n\nq.TableName                 // Todo\nq.KeyConditionExpression    // (Id = :k0)\nq.FilterExpression          // Done = :true\nq.ExpressionAttributeValues // :k0 = AttributeValue {N=1}, :true = AttributeValue {BOOL=true}\n```\n\nFilter expressions are applied after the query is executed which enable more flexible querying as they're not just \nlimited to key fields and can be used to query any field to further filter the returned resultset.\n\n### Executing Queries\n\nAfter you've finished populating the Request DTO it can be executed with PocoDynamo's `Query()`. This returns a lazily \nevaluated resultset which you can use LINQ methods on to fetch the results. Given the primary key condition we know this \nwill only return 0 or 1 rows based on whether or not the TODO has been completed which we can check with by calling \nLINQ's `FirstOrDefault()` method:\n\n```csharp\nvar todo1Done = db.Query(q).FirstOrDefault();\n```\n\nWhere `todo1Done` will hold the populated `Todo` if it was marked done, otherwise it will be `null`.\n\n#### Expression Chaining\n\nMost `QueryExpression` methods returns itself and an alternative to calling `Query` on PocoDynamo (or AWSSDK) to execute \nthe Query, you can instead call the `Exec()` alias. This allows you to create and execute your DynamoDb Query in a \nsingle expression which can instead be rewritten as:\n\n```csharp\nvar todo1Done = db.FromQuery\u003cTodo\u003e(x =\u003e x.Id == 1)\n    .Filter(x =\u003e x.Done)\n    .Exec()\n    .FirstOrDefault();\n```\n\n### Scan Operations\n\nScan Operations work very similar to Query Operations but instead of using a `QueryExpression\u003cT\u003e` you would instead use a `ScanExpression\u003cT\u003e` which as it inherits from AWSSDK's `ScanRequest` Request DTO, provides the same reuse benefits as QueryExpression's. \n\nTo create a Scan Request you would use the `FromScan\u003cT\u003e` API, e.g:\n\n```csharp\nvar q = db.FromScan\u003cTodo\u003e();\n```\n\nMore examples of how to use typed LINQ expressions for creating and executing Query and Scan requests are described later.\n\n### Related Items\n\nDynamoDB Queries are ideally suited for when the dataset is naturally isolated, e.g. multi-tenant Apps that are \ncentered around Customer data so any related records are able to share the same `CustomerId` Hash Key. \n\nPocoDynamo has good support for maintaining related data which can re-use the same Data Annotations used to define \nPOCO relationships in OrmLite, often letting you reuse your existing OrmLite RDBMS data models in DynamoDB as well.\n\nTo illustrate how to use PocoDynamo to maintain related data we'll walk through a typical Customer and Orders example:\n\n```csharp\npublic class Customer\n{\n    [AutoIncrement]\n    public int Id { get; set; }\n    public string Name { get; set; }\n    public CustomerAddress PrimaryAddress { get; set; }\n}\n\npublic class CustomerAddress\n{\n    [AutoIncrement]\n    public int Id { get; set; }\n    public string Address { get; set; }\n    public string State { get; set; }\n    public string Country { get; set; }\n}\n\n[Alias(\"CustomerOrder\")]\npublic class Order\n{\n    [AutoIncrement]\n    public int Id { get; set; }\n\n    [References(typeof(Customer))]\n    public int CustomerId { get; set; }\n\n    public string Product { get; set; }\n    public int Qty { get; set; }\n\n    [Index]\n    public virtual decimal Cost { get; set; }\n}\n```\n\nIn order to use them we need to tell PocoDynamo which of the Types are Tables that it should create in DynamoDB which\nwe can do by registering them with PocoDynamo then calling `InitSchema()` which will go through and create any\nof the tables that don't yet exist in DynamoDB: \n\n```csharp\ndb = new PocoDynamo(awsDb)\n    .RegisterTable\u003cCustomer\u003e()\n    .RegisterTable\u003cOrder\u003e();\n\ndb.InitSchema();\n```\n\n`InitSchema()` will also wait until the tables have been created so they're immediately accessible afterwards. \nAs creating DynamoDB tables can take upwards of a minute in AWS you can use the \n[alternative Async APIs](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/IPocoDynamoAsync.cs)\nif you wanted to continue to doing other stuff whilst the tables are being created in AWS, e.g:\n\n```csharp\nvar task = db.InitSchemaAsync();\n\n// do other stuff...\n\nawait task;\n```\n\n## Related Data\n\nAfter the tables are created we can insert the top-level Customer record as normal:\n\n```csharp\nvar customer = new Customer\n{\n    Name = \"Customer\",\n    PrimaryAddress = new CustomerAddress\n    {\n        Address = \"1 road\",\n        State = \"NT\",\n        Country = \"Australia\",\n    }\n};\n\ndb.PutItem(customer);\n```\n\nBefore adding the record, PocoDynamo also populates any `[AutoIncrement]` properties with the next number in the \nsequence for that Type. Any complex types stored on the `Customer` POCO like `CustomerAddress` gets persisted along\nwith the containing `Customer` entry and converted into a **Map** of DynamoDB Attribute Value pairs. We can view the \n[DynamoDB Web Console](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ConsoleDynamoDB.html) \nto see how this is stored in DynamoDB:\n\n![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/related-customer.png)\n\n### Related Tables\n\nYou can define a related table using the `[References]` attribute to tell PocoDynamo what the parent table is, e.g:\n\n```csharp\n[Alias(\"CustomerOrder\")]\npublic class Order\n{\n    [AutoIncrement]\n    public int Id { get; set; }\n    \n    [References(typeof(Customer))]\n    public int CustomerId { get; set; }   \n    //...\n}\n```\n\nWhich PocoDynamo infers to create the table using the parent's `CustomerId` as its Hash Key, relegating its `Id` as\nthe Range Key for the table. This ensures the Order is kept in the same partition as all other related Customer Data, \nnecessary in order to efficiently query a Customer's Orders. When both the Hash and Range Key are defined they're treated \nas the Composite Key for that table which needs to be unique for each item - guaranteed when using `[AutoIncrement]` Id's.\n\n#### Inserting Related Data\n\nAfter the table is created we can generate and insert random orders like any other table, e.g:\n\n```csharp\nvar orders = 10.Times(i =\u003e new Order\n{\n    CustomerId = customer.Id,\n    Product = \"Item \" + (i % 2 == 0 ? \"A\" : \"B\"),\n    Qty = i + 2,\n    Cost = (i + 2) * 2\n});\n\ndb.PutItems(orders);\n```\n\nYou can also use the alternative `PutRelatedItems()` API and get PocoDynamo to take care of populating the `CustomerId`:\n\n```csharp\nvar orders = 10.Times(i =\u003e new Order\n{\n    Product = \"Item \" + (i % 2 == 0 ? \"A\" : \"B\"),\n    Qty = i + 2,\n    Cost = (i + 2) * 2\n});\n\ndb.PutRelatedItems(customer.Id, orders);\n```\n\nBoth examples results in the same data being inserted into the **CustomerOrder** DynamoDB table:\n\n![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/related-customer-orders.png)\n\nThis also shows how the `[Alias]` attribute can be used to rename the `Order` Type as **CustomerOrder** in DynamoDB.\n\n### Querying Related Tables\n\nNow we have related data we can start querying it, something you may want to do is fetch all Customer Orders:\n\n```csharp\nvar q = db.FromQuery\u003cOrder\u003e(x =\u003e x.CustomerId == customer.Id);\nvar dbOrders = db.Query(q);\n```\n\nAs getting related Items for a Hash Key is a popular query, it has an explicit API:\n\n```csharp\nvar dbOrders = db.GetRelatedItems\u003cOrder\u003e(customer.Id);\n```\n\nWe can refine the query further by specifying a `FilterExpression` to limit the results DynamoDB returns:\n\n```csharp\nvar expensiveOrders = q.Clone()\n    .Filter(x =\u003e x.Cost \u003e 10)\n    .Exec();\n```\n\n\u003e Using `Clone()` will create and modify a copy of the query, leaving the original one intact.\n\n### [Local Secondary Indexes](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html)\n\nBut filters aren't performed on an Index and can be inefficient if your table has millions of customer rows. By default \nonly the Hash and Range Key are indexed, in order to efficiently query any other field you will need to create\na [Local Secondary Index](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html) for it.\n\nThis is easily done in PocoDynamo by annotating the properties you want indexed with the `[Index]` attribute:\n\n```csharp\npublic class Order\n{\n    //...    \n    [Index]\n    public decimal Cost { get; set; }\n}\n```\n\nWhich tells PocoDynamo to create a Local Secondary Index for the `Cost` property when it creates the table.\n\nWhen one exists, you can query a Local Index with `LocalIndex()`:\n\n```csharp\nvar expensiveOrders = q\n    .LocalIndex(x =\u003e x.Cost \u003e 10)\n    .Exec();    \n```\n\nWhich now performs the Cost query on an index. Although this only returns a partially populated Order, specifically\nwith just the Hash Key (CustomerId), Range Key (Id) and the field that's indexed (Cost):\n\n    expensiveOrders.PrintDump();\n    [\n        {\n            Id: 5,\n            CustomerId: 1,\n            Qty: 0,\n            Cost: 12\n        },\n        //...\n    ]\n\nThis is due to [Local Secondary Indexes](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html)\nbeing just denormalized tables behind the scenes which by default only returns re-projected fields that were defined\nwhen the Index was created. \n\nOne way to return populated orders is to specify a custom `ProjectionExpression` with the fields you want returned.\nE.g. You can create a request with a populated `ProjectionExpression` that returns all Order fields with:\n\n```csharp\nvar expensiveOrders = q\n    .LocalIndex(x =\u003e x.Cost \u003e 10)\n    .Select\u003cOrder\u003e()              //Equivalent to: SelectTableFields()\n    .Exec();\n```\n\nWhich now returns:\n\n    expensiveOrders.PrintDump();\n    [\n        {\n            Id: 5,\n            CustomerId: 1,\n            Product: Item A,\n            Qty: 6,\n            Cost: 12\n        },\n        //...\n    ]\n\n### Typed Local Indexes\n\nUsing a custom `ProjectionExpression` is an easy work-around, although for it to work DynamoDB needs to consult the\nprimary table to fetch the missing fields for each item. For large tables that are frequently accessed, the query\ncan be made more efficient by projecting the fields you want returned when the Index is created. \n\nYou can can tell PocoDynamo which additional fields it should reproject by creating a **Typed Local Index** which is \njust a POCO implementing `ILocalIndex\u003cT\u003e` containing all the fields the index should contain, e.g:\n\n```csharp\npublic class OrderCostLocalIndex : ILocalIndex\u003cOrder\u003e\n{\n    [Index]\n    public decimal Cost { get; set; }\n    public int CustomerId { get; set; }\n\n    public int Id { get; set; }\n    public int Qty { get; set; }\n}\n\n[References(typeof(OrderCostLocalIndex))]\npublic class Order { ... }\n```\n\nThen use the `[References]` attribute to register the Typed Index so PocoDynamo knows which additional indexes needs \nto be created with the table. The `[Index]` attribute is used to specify which field is indexed (Range Key) whilst \nthe `CustomerId` is automatically used the Hash Key for the Local Index Table.\n\n#### Querying Typed Indexes\n\nTo query a typed Index, use `FromQueryIndex\u003cT\u003e()` which returns a populated Query Request with the Table and Index Name.\nAs `Cost` is now the Range Key of the Local Index table it can be queried together with the `CustomerId` Hash Key in \nthe Key Condition expression:\n\n```csharp\nList\u003cOrderCostLocalIndex\u003e expensiveOrderIndexes = db.FromQueryIndex\u003cOrderCostLocalIndex\u003e(x =\u003e \n        x.CustomerId == customer.Id \u0026\u0026 x.Cost \u003e 10)\n    .Exec();\n```\n\nThis returns a list of populated indexes that now includes the `Qty` field:\n\n    expensiveOrderIndexes.PrintDump();\n    [\n        {\n            Cost: 12,\n            CustomerId: 1,\n            Id: 5,\n            Qty: 6\n        },\n        //...\n    ]\n\nIf preferred you can easily convert Typed Index into Orders by using ServiceStack's \n[built-in Auto-Mapping](https://github.com/ServiceStack/ServiceStack/wiki/Auto-mapping), e.g:\n\n```csharp\nList\u003cOrder\u003e expensiveOrders = expensiveOrderIndexes\n    .Map(x =\u003e x.ConvertTo\u003cOrder\u003e());\n```\n\n### [Global Secondary Indexes](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html)\n\nThe major limitation of Local Indexes is that they're limited to querying data in the same partition (Hash Key). \nTo efficiently query an index spanning the entire dataset, you need to instead use a \n[Global Secondary Index](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html).\n\nSupport for Global Indexes in PocoDynamo is similar to Typed Local Indexes, but instead implements `IGlobalIndex\u003cT\u003e`.\nThey also free you to choose a new Hash Key, letting you create an Index spanning all Customers. \n\nFor example we can create a global index that lets us search the cost across all orders containing a particular product:\n\n```csharp\npublic class OrderCostGlobalIndex : IGlobalIndex\u003cOrder\u003e\n{\n    [HashKey]\n    public string Product { get; set; }\n    [Index]\n    public decimal Cost { get; set; }\n\n    public int CustomerId { get; set; }\n    public int Qty { get; set; }\n    public int Id { get; set; }\n}\n\n[References(typeof(OrderCostGlobalIndex))]\npublic class Order { ... }\n```\n\nOur Key Condition can now instead query Product and Cost fields across all Customer Orders:\n\n```csharp\nvar expensiveItemAOrders = db.FromQueryIndex\u003cOrderCostGlobalIndex\u003e(x =\u003e \n        x.Product == \"Item A\" \u0026\u0026 x.Cost \u003e 10)\n    .Exec();\n```\n\nWhich will print all **Item A** Orders with a **Cost \u003e 10**:\n\n    expensiveItemAOrders.PrintDump();\n    [\n        {\n            Product: Item A,\n            Cost: 12,\n            CustomerId: 1,\n            Qty: 6,\n            Id: 5\n        },\n        //...\n    ]\n    \n## Scan Requests\n\nYou'll want to just use queries for any frequently accessed code running in production, although the full querying \nflexibility available in full table scan requests can be useful for ad hoc querying and to speed up development cycles\nby initially starting with Scan queries then when the data requirements for your App's have been finalized, rewrite \nthem to use indexes and queries.\n\nTo create Scan Requests you instead call the `FromScan*` API's, e.g:\n\n```csharp\nvar allOrders = db.ScanAll\u003cOrder\u003e();\n\nvar expensiveOrders = db.FromScan\u003cOrder\u003e(x =\u003e x.Cost \u003e 10)\n    .Exec();\n```\n\nYou can also perform scans on Global Indexes, but unlike queries they don't need to be limited to the Hash Key:\n\n```csharp\nvar expensiveOrderIndexes = db\n    .FromScanIndex\u003cOrderCostGlobalIndex\u003e(x =\u003e x.Cost \u003e 10)\n    .Exec();\n```\n\nJust like `QueryExpression\u003cT\u003e` the populated `ScanExpression\u003cT\u003e` inherits from AWSSDK's `ScanRequest` enabling the same\nre-use benefits for `ScanRequest` as they do for QueryRequest's.\n\n## Query and Scan Expressions\n\nBoth Scans and Query expressions benefit from a Typed LINQ-like expression API which can be used to populate the DTO's\n\n - **KeyConditionExpression** - for specifying conditions on tables Hash and Range keys (only: QueryRequest)\n - **FilterExpression** - for specifying conditions to filter results on other fields\n - **ProjectionExpression** - to specify any custom fields (default: all fields)\n\nEach `QueryRequest` needs to provide a key condition which can be done when creating the QueryExpression:\n\n```csharp\nvar orders = db.FromQuery\u003cOrder\u003e(x =\u003e x.CustomerId == 1).Exec();\n\n// Alternative explicit API\nvar expensiveOrders = db.FromQuery\u003cOrder\u003e().KeyCondition(x =\u003e x.CustomerId == 1).Exec();\n```\n\nWhilst every condition on a `ScanRequest` is added to the FilterExpression: \n\n```csharp\nvar expensiveOrders = db.FromScan\u003cOrder\u003e(x =\u003e x.Cost \u003e 10).Exec();\n\n// Alternative explicit API\nvar expensiveOrders = db.FromScan\u003cOrder\u003e().Filter(x =\u003e x.Cost \u003e 10).Exec();\n```\n\nCalling `Exec()` returns a lazily executed response which transparently sends multiple paged requests to fetch the \nresults as needed, e.g calling LINQ's `.FirstOrDefault()` only makes a single request whilst `.ToList()` fetches the \nentire resultset. All streaming `IEnumerable\u003cT\u003e` requests are sent with the configured `PagingLimit` (default: 1000).\n\n#### Custom Limits\n\nSeveral of PocoDynamo API's have overloads that let you specify a custom limit. API's with limits are instead \nexecuted immediately with the limit specified and returned in a concrete List:\n\n```csharp\nList\u003cOrder\u003e expensiveOrders = db.FromScan\u003cOrder\u003e().Filter(x =\u003e x.Cost \u003e 10).Exec(limit:5);\n```\n\n### Custom Filter Expressions\n\nThere are also custom overloads that can be used to execute a custom expression when more flexibility is needed:\n\n```csharp\n// Querying by Custom Filter Condition with anon args\nvar expensiveOrders = db.FromScan\u003cOrder\u003e().Filter(\"Cost \u003e :amount\", new { amount = 10 }).Exec();\n\n// Querying by Custom Filter Condition with loose-typed Dictionary\nvar expensiveOrders = db.FromScan\u003cOrder\u003e().Filter(\"Cost \u003e :amount\", \n        new Dictionary\u003cstring, object\u003e { { \"amount\", 10 } })\n    .Exec();\n```\n\n### Custom Select Projections\n\nBy default queries return all fields defined on the POCO model. You can also customize the projected fields that are \nreturned with the `Select*` and `Exec*` APIs:\n\n```csharp\n// Return partial fields from anon object\nvar partialOrders = db.FromScan\u003cOrder\u003e().Select(x =\u003e new { x.CustomerId, x.Cost }).Exec();\n\n// Return partial fields from array\nvar partialOrders = db.FromScan\u003cOrder\u003e().Select(x =\u003e new[] { \"CustomerId\", \"Cost\" }).Exec();\n\n// Return partial fields defined in a custom Poco\nclass CustomerCost\n{\n    public int CustomerId { get; set; }\n    public virtual decimal Cost { get; set; }\n}\n\nvar custCosts = db.FromScan\u003cOrder\u003e().Select\u003cCustomerCost\u003e()\n    .Exec()\n    .Map(x =\u003e x.ConvertTo\u003cCustomerCost\u003e());\n\n// Alternative shorter version of above\nvar custCosts = db.FromScan\u003cOrder\u003e().ExecInto\u003cCustomerCost\u003e().ToList();\n\n// Useful when querying and index and returing results in primary Order Poco \nList\u003cOrder\u003e expensiveOrders = db.FromScanIndex\u003cOrderCostGlobalIndex\u003e(x =\u003e x.Cost \u003e 10)\n    .ExecInto\u003cOrder\u003e();\n\n// Return a single column of fields\nList\u003cint\u003e orderIds = db.FromScan\u003cOrder\u003e().ExecColumn(x =\u003e x.Id).ToList();\n```\n\n### Advanced LINQ Expressions\n\nIn addition to basic predicate conditions, DynamoDB also includes support for \n[additional built-in functions](http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.SpecifyingConditions.html)\nwhich PocoDynamo also provides typed LINQ support for:\n\n#### begins_with\n\nReturn items where string fields starts with a particular substring:\n\n```csharp\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e x.Product.StartsWith(\"Item A\")).Exec();\n\n// Equivalent to\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.BeginsWith(x.Product, \"Item A\")).Exec();\n\nvar orders = db.FromScan\u003cOrder\u003e().Filter(\"begins_with(Product, :s)\", new { s = \"Item A\" }).Exec();\n```\n\n#### contains\n\nReturn items where string fields contains a particular substring:\n\n```csharp\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e x.Product.Contains(\"em A\")).Exec();\n\n// Equivalent to\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.Contains(x.Product, \"em A\")).Exec();\n\nvar orders = db.FromScan\u003cOrder\u003e().Filter(\"contains(Product, :s)\", new { s = \"em A\" }).Exec();\n```\n\n#### in\n\nReturns items where fields exist in a particular collection:\n\n```csharp\nvar qtys = new[] { 5, 10 };\n\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e qtys.Contains(x.Qty)).Exec();\n\n// Equivalent to\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.In(x.Qty, qtys)).Exec();\n\nvar orders = db.FromScan\u003cOrder\u003e().Filter(\"Qty in(:q1,:q2)\", new { q1 = 5, q2 = 10 }).Exec();\n```\n\n#### size\n\nReturns items where the string length equals a particular size:\n\n```csharp\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e x.Product.Length == 6).Exec();\n\n// Equivalent to\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.Size(x.Product) == 6).Exec();\n\nvar orders = db.FromScan\u003cOrder\u003e().Filter(\"size(Product) = :n\", new { n = 6 }).Exec();\n```\n\nSize also works for querying the size of different native DynamoDB collections, e.g:\n\n```csharp\npublic class IntCollections\n{\n    public int Id { get; set; }\n\n    public int[] ArrayInts { get; set; }\n    public HashSet\u003cint\u003e SetInts { get; set; }\n    public List\u003cint\u003e ListInts { get; set; }\n    public Dictionary\u003cint, int\u003e DictionaryInts { get; set; }\n}\n\nvar results = db.FromScan\u003cIntCollections\u003e(x =\u003e\n        x.ArrayInts.Length == 10 \u0026\u0026\n        x.SetInts.Count == 10 \u0026\u0026\n        x.ListInts.Count == 10 \u0026\u0026\n        x.DictionaryInts.Count == 10)\n    .Exec();\n```\n\n#### between\n\nReturns items where field values fall within a particular range (inclusive):\n\n```csharp\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.Between(x.Qty, 3, 5)).Exec();\n\n// Equivalent to\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e x.Qty \u003e= 3 \u0026\u0026 x.Qty \u003c= 5).Exec();\n\nvar orders = db.FromScan\u003cOrder\u003e().Filter(\"Qty between :from and :to\", new { from = 3, to = 5 }).Exec();\n```\n\n#### attribute_type\n\nReturn items where field is of a particular type:\n\n```csharp\nvar orders = db.FromScan\u003cOrder\u003e(x =\u003e \n        Dynamo.AttributeType(x.Qty, DynamoType.Number) \u0026\u0026\n        Dynamo.AttributeType(x.Product, DynamoType.String))\n    .Exec();\n\n// Equivalent to\nvar orders = db.FromScan\u003cOrder\u003e().Filter(\n        \"attribute_type(Qty, :n) and attribute_type(Product, :s)\", new { n = \"N\", s = \"S\"})\n    .Exec();\n```\n\nValid Types: L (List), M (Map), S (String), SS (StringSet), N (Number), NS (NumberSet), B (Binary), BS, BOOL, NULL\n\n#### attribute_exists\n\nReturn items where a particular field exists. As the schema of your data models evolve you can use this to determine\nwhether items are of an old or new schema:\n\n```csharp\nvar newOrderTypes = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.AttributeExists(x.NewlyAddedField)).Exec();\n\n// Equivalent to\nvar newOrderTypes = db.FromScan\u003cOrder\u003e().Filter(\"attribute_exists(NewlyAddedField)\").Exec();\n```\n\n#### attribute_not_exists\n\nReturn items where a particular field does not exist:\n\n```csharp\nvar oldOrderTypes = db.FromScan\u003cOrder\u003e(x =\u003e Dynamo.AttributeNotExists(x.NewlyAddedField)).Exec();\n\n// Equivalent to\nvar oldOrderTypes = db.FromScan\u003cOrder\u003e().Filter(\"attribute_not_exists(NewlyAddedField)\").Exec();\n```\n\n### Defaults and Custom Behavior\n\nPocoDynamo is configured with the defaults below which it uses throughout its various API's when used in creating and \nquerying tables:\n\n```csharp\n//Defaults:\nvar db = new PocoDynamo(awsDb) {\n    PollTableStatus = TimeSpan.FromSeconds(2),\n    MaxRetryOnExceptionTimeout = TimeSpan.FromSeconds(60),\n    ReadCapacityUnits = 10,\n    WriteCapacityUnits = 5,\n    ConsistentRead = true,\n    ScanIndexForward = true,\n    PagingLimit = 1000,\n};\n```\n\nIf you wanted to query with different behavior you can create a clone of the client with the custom settings you want,\ne.g. you can create a client that performs eventually consistent queries with:\n\n```csharp\nIPocoDynamo eventuallyConsistentDb = db.ClientWith(consistentRead:false);\n```\n\n## Table definition\n\nTo support different coding styles, readability/dependency preferences and levels of data model reuse, PocoDynamo \nenables a wide array of options for specifying a table's Hash and Range Keys, in the following order or precedence:\n\n**Note: Hash and Range keys cannot be read-only calculated properties.\n\n### Specifying a Hash Key\n\nUsing the AWSSDK's `[DynamoDBHashKey]` attribute:\n\n```csharp\npublic class Table\n{\n    [DynamoDBHashKey]\n    public int CustomId { get; set; }\n}\n```\n\nThis requires your models to have a dependency to the **AWSSDK.DynamoDBv2** NuGet package which can be avoided by using \n**ServiceStack.Interfaces** `[HashKey]` attribute instead which your models already likely have a reference to:\n\n```csharp\npublic class Table\n{\n    [HashKey]\n    public int CustomId { get; set; }\n}\n```\n\nYou can instead avoid any attributes using the explicit **HashKey** Naming convention:\n\n```csharp\npublic class Table\n{\n    public int HashKey { get; set; }\n}\n```\n\nFor improved re-usability of your models you can instead use the generic annotations for defining a model's primary key:\n\n```csharp\npublic class Table\n{\n    [PrimaryKey]\n    public int CustomId { get; set; }\n}\n```\n\n```csharp\npublic class Table\n{\n    [AutoIncrement]\n    public int CustomId { get; set; }\n}\n```\n\nAlternative using the Universal `Id` naming convention:\n\n```csharp\npublic class Table\n{\n    public int Id { get; set; }\n}\n```\n\nIf preferred both Hash and Range Keys can be defined together with the class-level `[CompositeKey]` attribute:\n\n```csharp\n[CompositeKey(\"CustomHash\", \"CustomRange\")]\npublic class Table\n{\n    public int CustomHash { get; set; }\n    public int CustomRange { get; set; }\n}\n```\n\n### Specifying a Range Key\n\nFor specifying the Range Key use can use the **AWSSDK.DynamoDBv2** Attribute:\n\n```csharp\npublic class Table\n{\n    [DynamoDBRangeKey]\n    public int CustomId { get; set; }\n}\n```\n\nThe **ServiceStack.Interfaces** attribute:\n\n```csharp\npublic class Table\n{\n    [RangeKey]\n    public int CustomId { get; set; }\n}\n```\n\nOr without attributes, using the explicit `RangeKey` property name:\n\n```csharp\npublic class Table\n{\n    public int RangeKey { get; set; }\n}\n```\n\n## Examples\n\n### [DynamoDbCacheClient](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/DynamoDbCacheClient.cs)\n\nWe've been quick to benefit from the productivity advantages of PocoDynamo ourselves where we've used it to rewrite\n[DynamoDbCacheClient](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/DynamoDbCacheClient.cs)\nwhich is now just 2/3 the size and much easier to maintain than the existing \n[Community-contributed version](https://github.com/ServiceStack/ServiceStack/blob/22aca105d39997a8ea4c9dc20b242f78e07f36e0/src/ServiceStack.Caching.AwsDynamoDb/DynamoDbCacheClient.cs)\nwhilst at the same time extending it with even more functionality where it now implements the `ICacheClientExtended` API.\n\n### [DynamoDbAuthRepository](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/DynamoDbAuthRepository.cs)\n\nPocoDynamo's code-first Typed API made it much easier to implement value-added DynamoDB functionality like the new\n[DynamoDbAuthRepository](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/DynamoDbAuthRepository.cs)\nwhich due sharing a similar code-first POCO approach to OrmLite, ended up being a straight-forward port of the existing\n[OrmLiteAuthRepository](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Server/Auth/OrmLiteAuthRepository.cs)\nwhere it was able to reuse the existing `UserAuth` and `UserAuthDetails` data models.\n\n### [DynamoDbTests](https://github.com/ServiceStack/ServiceStack.Aws/tree/master/tests/ServiceStack.Aws.DynamoDbTests)\n\nDespite its young age we've added a comprehensive test suite behind PocoDynamo which has become our exclusive client\nfor developing DynamoDB-powered Apps.\n\n### [AWS Apps](http://awsapps.servicestack.net/)\n\nThe [Live Demos](https://github.com/ServiceStackApps/LiveDemos) below were rewritten from their original RDBMS and OrmLite\nbackends to utilize a completely managed AWS Stack that now uses PocoDynamo and a DynamoDB-backend:\n\n[![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/examples-razor-rockstars.png)](http://awsrazor.servicestack.net/)\n\n[![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/examples-email-contacts.png)](http://awsapps.servicestack.net/emailcontacts/)\n\n[![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/examples-todos.png)](http://awsapps.servicestack.net/todo/)\n\n[![](https://raw.githubusercontent.com/ServiceStack/Assets/master/img/aws/pocodynamo/examples-awsauth.png)](http://awsapps.servicestack.net/awsauth/)\n\n## IPocoClient API\n\n```csharp\n// Interface for the code-first PocoDynamo client\npublic interface IPocoDynamo : IPocoDynamoAsync, IRequiresSchema\n{\n    // Get the underlying AWS DynamoDB low-level client\n    IAmazonDynamoDB DynamoDb { get; }\n\n    // Get the numeric unique Sequence generator configured with this client\n    ISequenceSource Sequences { get; }\n\n    // Access the converters that converts POCO's into DynamoDB data types\n    DynamoConverters Converters { get; }\n\n    // How long should PocoDynamo keep retrying failed operations in an exponential backoff (default 60s)\n    TimeSpan MaxRetryOnExceptionTimeout { get; }\n\n    // Get the AWSSDK DocumentModel schema for this Table\n    Table GetTableSchema(Type table);\n\n    // Get PocoDynamo Table metadata for this table\n    DynamoMetadataType GetTableMetadata(Type table);\n\n    // Calls 'ListTables' to return all Table Names in DynamoDB\n    IEnumerable\u003cstring\u003e GetTableNames();\n\n    // Creates any tables missing in DynamoDB from the Tables registered with PocoDynamo\n    bool CreateMissingTables(IEnumerable\u003cDynamoMetadataType\u003e tables, TimeSpan? timeout = null);\n\n    // Creates any tables missing from the specified list of tables\n    bool CreateTables(IEnumerable\u003cDynamoMetadataType\u003e tables, TimeSpan? timeout = null);\n\n    // Deletes all DynamoDB Tables\n    bool DeleteAllTables(TimeSpan? timeout = null);\n\n    // Deletes the tables in DynamoDB with the specified table names\n    bool DeleteTables(IEnumerable\u003cstring\u003e tableNames, TimeSpan? timeout = null);\n\n    // Gets the POCO instance with the specified hash\n    T GetItem\u003cT\u003e(object hash);\n\n    // Gets the POCO instance with the specified hash and range value\n    T GetItem\u003cT\u003e(object hash, object range);\n\n    // Calls 'BatchGetItem' in the min number of batch requests to return POCOs with the specified hashes \n    List\u003cT\u003e GetItems\u003cT\u003e(IEnumerable\u003cobject\u003e hashes);\n\n    // Calls 'PutItem' to store instance in DynamoDB\n    T PutItem\u003cT\u003e(T value, bool returnOld = false);\n\n    // Calls 'BatchWriteItem' to efficiently store items in min number of batched requests\n    void PutItems\u003cT\u003e(IEnumerable\u003cT\u003e items);\n\n    // Deletes the instance at the specified hash\n    T DeleteItem\u003cT\u003e(object hash, ReturnItem returnItem = ReturnItem.None);\n\n    // Calls 'BatchWriteItem' to efficiently delete all items with the specified hashes\n    void DeleteItems\u003cT\u003e(IEnumerable\u003cobject\u003e hashes);\n\n    // Calls 'BatchWriteItem' to efficiently delete all items with the specified hash and range pairs\n    void DeleteItems\u003cT\u003e(IEnumerable\u003cDynamoId\u003e hashes);\n\n    // Calls 'UpdateItem' with ADD AttributeUpdate to atomically increment specific field numeric value\n    long Increment\u003cT\u003e(object hash, string fieldName, long amount = 1);\n\n    // Polls 'DescribeTable' until all Tables have an ACTIVE TableStatus\n    bool WaitForTablesToBeReady(IEnumerable\u003cstring\u003e tableNames, TimeSpan? timeout = null);\n\n    // Polls 'ListTables' until all specified tables have been deleted\n    bool WaitForTablesToBeDeleted(IEnumerable\u003cstring\u003e tableNames, TimeSpan? timeout = null);\n\n    // Updates item Hash field with hash value then calls 'PutItem' to store the related instance\n    void PutRelatedItem\u003cT\u003e(object hash, T item);\n\n    // Updates all item Hash fields with hash value then calls 'PutItems' to store all related instances\n    void PutRelatedItems\u003cT\u003e(object hash, IEnumerable\u003cT\u003e items);\n\n    // Calls 'Query' to return all related Items containing the specified hash value\n    IEnumerable\u003cT\u003e GetRelatedItems\u003cT\u003e(object hash);\n\n    // Deletes all items with the specified hash and ranges\n    void DeleteRelatedItems\u003cT\u003e(object hash, IEnumerable\u003cobject\u003e ranges);\n\n\n    // Calls 'Scan' to return lazy enumerated results that's transparently paged across multiple queries\n    IEnumerable\u003cT\u003e ScanAll\u003cT\u003e();\n\n    // Creates a Typed `ScanExpression` for the specified table\n    ScanExpression\u003cT\u003e FromScan\u003cT\u003e(Expression\u003cFunc\u003cT, bool\u003e\u003e filterExpression = null);\n\n    // Creates a Typed `ScanExpression` for the specified Global Index\n    ScanExpression\u003cT\u003e FromScanIndex\u003cT\u003e(Expression\u003cFunc\u003cT, bool\u003e\u003e filterExpression = null);\n\n    // Executes the `ScanExpression` returning the specified maximum limit of results\n    List\u003cT\u003e Scan\u003cT\u003e(ScanExpression\u003cT\u003e request, int limit);\n\n    // Executes the `ScanExpression` returning lazy results transparently paged across multiple queries\n    IEnumerable\u003cT\u003e Scan\u003cT\u003e(ScanExpression\u003cT\u003e request);\n\n    // Executes AWSSDK `ScanRequest` returning the specified maximum limit of results\n    List\u003cT\u003e Scan\u003cT\u003e(ScanRequest request, int limit);\n\n    // Executes AWSSDK `ScanRequest` returning lazy results transparently paged across multiple queries\n    IEnumerable\u003cT\u003e Scan\u003cT\u003e(ScanRequest request);\n\n    // Executes AWSSDK `ScanRequest` with a custom conversion function to map ScanResponse to results\n    IEnumerable\u003cT\u003e Scan\u003cT\u003e(ScanRequest request, Func\u003cScanResponse, IEnumerable\u003cT\u003e\u003e converter);\n\n\n    // Return Live ItemCount using Table ScanRequest\n    long ScanItemCount\u003cT\u003e();\n\n    // Return cached ItemCount in summary DescribeTable\n    long DescribeItemCount\u003cT\u003e();\n\n\n    // Creates a Typed `QueryExpression` for the specified table\n    QueryExpression\u003cT\u003e FromQuery\u003cT\u003e(Expression\u003cFunc\u003cT, bool\u003e\u003e keyExpression = null);\n\n    // Executes the `QueryExpression` returning lazy results transparently paged across multiple queries\n    IEnumerable\u003cT\u003e Query\u003cT\u003e(QueryExpression\u003cT\u003e request);\n\n    // Executes the `QueryExpression` returning the specified maximum limit of results\n    List\u003cT\u003e Query\u003cT\u003e(QueryExpression\u003cT\u003e request, int limit);\n\n    // Creates a Typed `QueryExpression` for the specified Local or Global Index\n    QueryExpression\u003cT\u003e FromQueryIndex\u003cT\u003e(Expression\u003cFunc\u003cT, bool\u003e\u003e keyExpression = null);\n\n    // Executes AWSSDK `QueryRequest` returning the specified maximum limit of results\n    List\u003cT\u003e Query\u003cT\u003e(QueryRequest request, int limit);\n\n    // Executes AWSSDK `QueryRequest` returning lazy results transparently paged across multiple queries\n    IEnumerable\u003cT\u003e Query\u003cT\u003e(QueryRequest request);\n\n    // Executes AWSSDK `QueryRequest` with a custom conversion function to map QueryResponse to results\n    IEnumerable\u003cT\u003e Query\u003cT\u003e(QueryRequest request, Func\u003cQueryResponse, IEnumerable\u003cT\u003e\u003e converter);\n\n\n    // Create a clone of the PocoDynamo client with different default settings\n    IPocoDynamo ClientWith(\n        bool? consistentRead = null,\n        long? readCapacityUnits = null,\n        long? writeCapacityUnits = null,\n        TimeSpan? pollTableStatus = null,\n        TimeSpan? maxRetryOnExceptionTimeout = null,\n        int? limit = null,\n        bool? scanIndexForward = null);\n\n    // Disposes the underlying IAmazonDynamoDB client\n    void Close();\n}\n\n// Available API's with Async equivalents\npublic interface IPocoDynamoAsync\n{\n    Task CreateMissingTablesAsync(IEnumerable\u003cDynamoMetadataType\u003e tables, \n        CancellationToken token = default(CancellationToken));\n\n    Task WaitForTablesToBeReadyAsync(IEnumerable\u003cstring\u003e tableNames, \n        CancellationToken token = default(CancellationToken));\n\n    Task InitSchemaAsync();\n}\n```\n\n### PocoDynamo Extension helpers\n\nTo maintain a minimumal surface area for PocoDynamo, many additional API's used to provide a more DRY typed API's were moved into\n[PocoDynamoExtensions](https://github.com/ServiceStack/ServiceStack.Aws/blob/master/src/ServiceStack.Aws/DynamoDb/PocoDynamoExtensions.cs)\n\n```csharp\nclass PocoDynamoExtensions\n{\n    //Register Table\n    DynamoMetadataType RegisterTable\u003cT\u003e();\n    DynamoMetadataType RegisterTable(Type tableType);\n    void RegisterTables(IEnumerable\u003cType\u003e tableTypes);\n    void AddValueConverter(Type type, IAttributeValueConverter valueConverter);\n\n    //Get Table Metadata\n    Table GetTableSchema\u003cT\u003e();\n    DynamoMetadataType GetTableMetadata\u003cT\u003e();\n\n    //Create Table\n    bool CreateTableIfMissing\u003cT\u003e();\n    bool CreateTableIfMissing(DynamoMetadataType table);\n    bool CreateTable\u003cT\u003e(TimeSpan? timeout = null);\n\n    bool DeleteTable\u003cT\u003e(TimeSpan? timeout = null);\n\n    //Decrement API's\n    long DecrementById\u003cT\u003e(object id, string fieldName, long amount = 1);\n    long IncrementById\u003cT\u003e(object id, Expression\u003cFunc\u003cT, object\u003e\u003e fieldExpr, long amount = 1);\n    long DecrementById\u003cT\u003e(object id, Expression\u003cFunc\u003cT, object\u003e\u003e fieldExpr, long amount = 1);\n\n    List\u003cT\u003e GetAll\u003cT\u003e();\n    T GetItem\u003cT\u003e(DynamoId id);\n\n    //Typed API overloads for popular hash object ids\n    List\u003cT\u003e GetItems\u003cT\u003e(IEnumerable\u003cint\u003e ids);\n    List\u003cT\u003e GetItems\u003cT\u003e(IEnumerable\u003clong\u003e ids);\n    List\u003cT\u003e GetItems\u003cT\u003e(IEnumerable\u003cstring\u003e ids);\n\n    void DeleteItems\u003cT\u003e(IEnumerable\u003cint\u003e ids);\n    void DeleteItems\u003cT\u003e(IEnumerable\u003clong\u003e ids);\n    void DeleteItems\u003cT\u003e(IEnumerable\u003cstring\u003e ids);\n\n    //Scan Helpers\n    IEnumerable\u003cT\u003e ScanInto\u003cT\u003e(ScanExpression request);\n    List\u003cT\u003e ScanInto\u003cT\u003e(ScanExpression request, int limit);\n\n    //Query Helpers\n    IEnumerable\u003cT\u003e QueryInto\u003cT\u003e(QueryExpression request);\n    List\u003cT\u003e QueryInto\u003cT\u003e(QueryExpression request, int limit);\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fservicestack%2Fpocodynamo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fservicestack%2Fpocodynamo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fservicestack%2Fpocodynamo/lists"}