在.NET项目里接入Amazon DynamoDB,最顺手的方式就是使用官方发布的AWSSDK.DynamoDBv2包。它位于Amazon.DynamoDBv2命名空间下,提供了比底层REST接口更友好的客户端模型,既可以用Document这种弱类型结构操作,也能通过ORM式的映射处理自定义实体类。

一、安装与基础配置
首先通过NuGet安装核心包,命令如下。除了主包之外,一般还需要AWSSDK.Core来处理凭证和HTTP管道。
dotnet add package AWSSDK.DynamoDBv2 dotnet add package AWSSDK.Core
配置客户端时,最常见的做法是使用AppSettings里的profile或者环境变量。下面代码展示如何显式构造AmazonDynamoDBClient并指向特定区域。如果你在本地用LocalStack做集成测试,把ServiceURL换成http://127.0.0.1:4566即可。
using Amazon.DynamoDBv2;
using Amazon.Runtime;
var config = new AmazonDynamoDBConfig
{
RegionEndpoint = Amazon.RegionEndpoint.USEast1,
// 本地调试用LocalStack时打开下面这行
// ServiceURL = "http://127.0.0.1:4566"
};
// 使用本机配置的default profile凭证
var credentials = new StoredProfileAWSCredentials("default");
var client = new AmazonDynamoDBClient(credentials, config);
这种写法把区域和凭证解耦,方便在多环境之间切换。需要注意的是,若部署到EC2或ECS,通常不需要在代码里写死凭证,SDK会自动从实例元数据获取IAM角色,此时直接new AmazonDynamoDBClient()就好。
另外,AWSSDK.DynamoDBv2内部使用HttpClient做请求,高并发场景下建议把client声明为单例,避免频繁建连导致端口耗尽。官方客户端本身是线程安全的,可以放心复用。
二、用DocumentModel读写数据
DocumentModel是位于Amazon.DynamoDBv2.DocumentModel命名空间下的一组类型,Table类是其入口。它不要求你提前定义C#类,适合表结构动态或者只是做简单工具的场景。
using Amazon.DynamoDBv2.DocumentModel; var table = Table.LoadTable(client, "UserOrders"); // 写入一条数据 var doc = new Document(); doc["UserId"] = "u_1001"; doc["OrderId"] = "o_2023_001"; doc["Amount"] = 199.9; doc["CreatedAt"] = "2023-08-01T10:00:00Z"; await table.PutItemAsync(doc); // 按主键读取 var key = new Document(); key["UserId"] = "u_1001"; key["OrderId"] = "o_2023_001"; var loaded = await table.GetItemAsync(key); Console.WriteLine(loaded["Amount"].AsDecimal());
上面代码里的UserId和OrderId组成了复合主键。PutItemAsync会整体替换原记录,如果想做局部更新,可以用UpdateItemAsync并指定更新表达式,不过DocumentModel对表达式支持不如强类型上下文直观。
Document的好处是字段可以随意增减,不用改实体类;缺点是所有字段名都是字符串,编译期发现不了拼写错误。在字段较多或者团队协作时,这种弱约束容易埋下bug。
三、使用强类型上下文(DynamoDBContext)
DynamoDBContext是AWSSDK.DynamoDBv2提供的高层ORM,借助特性标注把C#对象映射成表记录。它位于Amazon.DynamoDBv2.DataModel命名空间。
using Amazon.DynamoDBv2.DataModel;
[DynamoDBTable("UserOrders")]
public class UserOrder
{
[DynamoDBHashKey]
public string UserId { get; set; }
[DynamoDBRangeKey]
public string OrderId { get; set; }
public decimal Amount { get; set; }
public string CreatedAt { get; set; }
}
var context = new DynamoDBContext(client);
// 保存
await context.SaveAsync(new UserOrder
{
UserId = "u_1002",
OrderId = "o_2023_002",
Amount = 59.0m,
CreatedAt = "2023-08-02T09:30:00Z"
});
// 读取
var order = await context.LoadAsync<UserOrder>("u_1002", "o_2023_002");
通过方括号特性,我们明确标出哈希键和范围键。编译器现在能检查属性名,重构也更安全。Context还支持异步批量操作和索引查询,例如用QueryAsync配合DynamoDBOperationConfig指定二级索引名。
不过强类型也有代价:当表结构发生变更,比如新增一个索引字段,你需要同步改代码并重新发布。对于快速迭代的脚本类任务,DocumentModel反而更轻量。实际项目里常两者混用,核心业务用Context,运维工具用Document。
四、批量写入与二级索引查询
单条写在高并发下吞吐容易受限,AWSSDK.DynamoDBv2提供了BatchWriteItemAsync来一次性提交多张表的多条记录。
var batch = client.CreateBatchWrite();
batch.AddPutItem("UserOrders", new Document
{
["UserId"] = "u_2001",
["OrderId"] = "o_batch_1",
["Amount"] = 10m
});
batch.AddPutItem("UserOrders", new Document
{
["UserId"] = "u_2001",
["OrderId"] = "o_batch_2",
["Amount"] = 20m
});
await batch.ExecuteAsync();
二级索引查询在DocumentModel里用Query类实现。假设表上有一个Global Secondary Index叫CreatedAtIndex,可以用如下方式拉取某天的数据。
var query = table.Query(new QueryFilter("CreatedAt", QueryOperator.BeginsWith, "2023-08"));
query.IndexName = "CreatedAtIndex";
var docs = await query.GetRemainingAsync();
foreach (var d in docs)
{
Console.WriteLine(d["OrderId"].AsString());
}
需要留意的是,查询二级索引可能只返回投影字段,若索引未包含Amount,上面d["Amount"]会抛异常。设计表时要规划好索引投影策略,避免额外的一次主表回查。
五、常见误区与调试建议
一个常见坑是容量模式选择。新建表默认是按需模式,突发流量不会限流,但单价高;若用预置模式却低估了写入容量,就会收到限流异常。AWSSDK抛出的ProvisionedThroughputExceededException要明确捕获并重试。
另一个容易混淆的概念是,DynamoDBContext的SaveAsync并不总是等同于PutItem,当实体标记了版本字段或使用了条件保存时,它会转成带条件的写操作。
本地调试推荐用LocalStack起一个兼容端点,把ServiceURL指向127.0.0.1对应端口,这样不用真实扣费就能跑通整套CRUD逻辑。上线前再切回真实Region,并配合IAM策略最小授权,只允许对应表的读写操作。
整体来看,AWSSDK.DynamoDBv2在.NET生态里已经足够成熟,合理选择Document和Context两种模型,配合批量接口与索引设计,就能搭建出既灵活又稳健的数据访问层。
DynamoDBAWSSDK_DynamoDBv2NET修改时间:2026-08-10 16:30:57