首页 > 解决方案 > DynamoDB - 如何使用 ServiceStack.Aws 实现乐观锁定

问题描述

目前,我正在使用 ServiceStack.Aws v5.9.0 与 DynamoDB 进行通信。我已经使用 PutItem 来创建和更新项目,而不会在并发处理的情况下预期数据丢失。

public class Customer
{
   [HashKey]
   public int CustomerId { get; set; }
   [AutoIncrement]
   public int SubId { get; set; }
   public string CustomerType { get; set; }
   public string LastName { get; set; }
   public string FirstName { get; set; }
   ...//and hundreds of fields here
}

public class CustomerDynamo
{
   private readonly IPocoDynamo db;
   //Constructor
   public CustomerDynamo() 
   {
      var dynamoClient = new AmazonDynamoDBClient(_region);
      var entityType = typeof(Customer);
      var tableName = entityType.Name;
      entityType.AddAttributes(new AliasAttribute(name: tableName));
      db = new PocoDynamo(dynamoClient) { ConsistentRead = true }.RegisterTable(tableType: entityType);
   }

   public Customer Update(Customer customer)
   {
      customer.ModifiedDate = DateTime.UtcNow;
      db.PutItem(customer);
      return customer;
   }
}

在需要更新客户数据的每个服务/异步任务中都会调用上述 Update 方法。参考这篇 AWS 的文章,我决定实现 Optimistic Locking 来避免并发请求的问题。 https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBContext.VersionSupport.html

假设 VersionNumber 将是 Optimistic Locking 的关键。所以我将 VersionNumber 添加到 Customer 模型中。

public class Customer
{
   [HashKey]
   public int CustomerId { get; set; }
   [AutoIncrement]
   public int SubId { get; set; }
   public string CustomerType { get; set; }
   public string LastName { get; set; }
   public string FirstName { get; set; }
   ...//and hundreds of fields here
   [DynamoDBVersion]
   public int? VersionNumber { get; set; }
}
  1. 结果是 VersionNumber 未更新,而应自动递增。我认为这只是因为 PutItem 将覆盖整个现有项目。这个对吗?
  2. 我想我需要在 Update 方法中从 PutItem 更改为 UpdateItem。问题是如何动态生成表达式以与 UpdateItem 一起使用?

提前感谢您的帮助!

更新:

感谢@mythz提供有关 DynamoDBVersion 属性的有用信息。然后我尝试删除 DynamoDBVersion 并使用 PocoDynamo 的 UpdateExpression 如下

public Customer Update(Customer customer)
{
   customer.ModifiedDate = DateTime.UtcNow;
   var expression = db.UpdateExpression<Customer>(customer.CustomerId).Set(() => customer);
   expression.ExpressionAttributeNames = new Dictionary<string, string>() 
   { 
      { "#Version", "VersionNumber" } 
   };
   expression.ExpressionAttributeValues = new Dictionary<string, AttributeValue>() 
   {
      { ":incr", new AttributeValue { N = "1" } },
      { ":zero", new AttributeValue { N = "0" } }
   };
   expression.UpdateExpression = "SET #Version = if_not_exists(#Version, :zero) + :incr";
   if (customer.VersionNumber.HasValue)
   {
      expression.Condition(c => c.VersionNumber == customer.VersionNumber);
   }
   var success = db.UpdateItem(expression);
}

但是除了 VersionNumber 之外,没有保存更改

标签: c#amazon-dynamodbservicestackoptimistic-lockingpocodynamo

解决方案


[DynamoDBVersion]是一个 AWS Object Persistence Model 属性,用于 AWS 的DynamoDBContextnot for PocoDynamo。即[DynamoDB*]PocoDynamo 使用的唯一属性是[DynamoDBHashKey][DynamoDBRangeKey]所有其他[DynamoDB*]属性都适用于 AWS 的对象持久性模型库。

需要时,您可以通过以下方式访问 AWS IAmazonDynamoDB

var db = new PocoDynamo(awsDb);
var awsDb = db.DynamoDb;

以下是有关 PocoDynamo 的 UpdateItem API可能相关的文档。


推荐阅读