【问题标题】:Update Amplify Datastore items' value by adding / subtracting?通过加/减来更新 Amplify Datastore 项目的值?
【发布时间】:2021-12-16 19:50:06
【问题描述】:

我想使用数据存储根据存储在数据库中的当前值更新值。 在documentation 中,它显示了一个零售柜台用例。但是你应该如何实现这种设置呢?

一个小例子:

我有一个库存表,其中包含一个项目和一个项目的计数器:

type Inventory @model {
id: ID!
name: String!
count: Int!}

如果我想保存一个新的Inventory,我可以这样做:

Inventory inventory = Inventory(id,'product1',10);
Amplify.Datastore.save(inventory);

然后,我知道要更新我可以使用具有相同 ID 的 Amplify.Datastore.save(inventoryUpdatedModel),并且 Amplify 将更新存储中的项目。到目前为止一切顺利。

如果我有多个设备更改与示例中相同的值怎么办?

开始:count = 10;

1:设备 1 -> 计数加 5 (10 + 5 = 15)

2:设备 2 -> 从计数中减去 3 (10 - 3 = 7)

3:两个设备都更新 Datastore 并读取值。

目前,仅显示最新值(示例中为 7),其中所需答案为 12。(10+5-3 = 12)

到目前为止我试过了:

  1. 在保存之前查询数据库,但是当多个设备同时执行操作时,这会出现明显的并发问题。

  2. 添加一个名为 updateInventoryCount(inventoryID,numberToAdd) 的新表,然后仅在一台设备上一次处理此表中的数据一项。平衡对同一 ID 的任何更新的价值,然后将其删除实际上是可行的,但它远非完美的解决方案。

我喜欢Amplify.Datastore.updateValue(ID,-3); 放大 -> 取旧值,减去 3,用相同的命令更新 appsync

【问题讨论】:

    标签: flutter datastore amplify


    【解决方案1】:

    我选择的方向是这样的,如果我偏离了标记,请随时告诉我!

    基本上: 运行一项服务来检测与 AWS 同步的补充表中的更改,并且只允许此服务在本地更改数据,而不是允许 AWS 更改您在本地受保护的表。

    优点:

    • 根本不依赖任何服务器代码。
    • 间歇性网络很好,更新在多个设备上保持不变,
    • 仅使用颤振放大 api

    缺点:

    • 将向 AWS 创建更多写入
    • 很复杂

    制作模型:无法让模型直接通过 codegen'd 代码获取相关对象,(即使看起来有管道可以做到这一点..)所以我使用 'table行的 ID 并执行两个 Amplify.DataStore.query(model.classType) 操作以获取我的本地节点的 inventory,然后是通用的 ticketitem 数据

    type Inventory @model {
      id:ID!
      clientid: String!
      inventoryclientid: String!
      quantity:Int!
      sellprice: Float!
      ticketItemID: String!}
    
     type TicketItem @model {
          id:ID!
          name: String!
          buyprice: Float!
          genericnfo: String!
          }
    
    type SyncedMutations @model {
        id:ID!
        clientid: String!
        ownerclientid: String!
        tablename: String!
        itemid: String!
        mutation: String!
        from: String!
        completed: [String]
        itemdata: String!
    }
    

    然后: amplify codegen models

    我还必须像这样在启动时使用selectiveSync:

        AmplifyDataStore amplifyDataStore = AmplifyDataStore(modelProvider: 
        ModelProvider.instance, syncExpressions: [    
        DataStoreSyncExpression(SyncedMutations.classType, () => SyncedMutations.OWNERCLIENTID.eq(curClient))     
        DataStoreSyncExpression(Inventory.classType, () => Inventory.CLIENTID.eq(curClient)),
        ]);
        
          Amplify.addPlugin(amplifyDataStore);
    

    然后我在启动时设置一个服务来订阅和监听SyncedMutations 表发生的事件,并在任何创建操作上更新本地inventory 表。代码如下(需要清理一下):

    import ...
    
    @LazySingleton()
    class SyncMutationsService {
      static final SyncMutationsService _instance = SyncMutationsService._internal();
      final _clientService = locator<ClientService>();
    
      SyncMutationsService._internal() {
        Stream<SubscriptionEvent<SyncedMutations>> stream = Amplify.DataStore.observe(SyncedMutations.classType)
          ..listen(handleSubscription);
      }
    
      factory SyncMutationsService() => _instance;
    
      handleSubscription(SubscriptionEvent<SyncedMutations> event) async {
     if (event.eventType == EventType.create) {
        updateLocalProtectedTable(event.item);
        }
      }
    
      void updateLocalProtectedTable(SyncedMutations eventitem) {
        switch (eventitem.tablename) {
          case 'Inventory':
            print('Inventory table update.. ');
            updateInventoryOnEvent(eventitem);
            break;
        }
      }
    
      void updateInventoryOnEvent(SyncedMutations eventitem) {
        try {
          Inventory inventorydata = Inventory.fromJson(json.decode(eventitem.itemdata));
    
          SharedPreferencesHelper.getCurrentClientID().then((curClientID) {
            if (eventitem.completed == null || !eventitem.completed!.contains(curClientID)) {
              try {
                if (int.tryParse(eventitem.mutation) != null) {
                  getCorrespondingInventoryLine(inventorydata.inventoryTicketItemID).then((oldLocalInvItem) {
                    Inventory updatedInventoryItem = oldInvItem.copyWith(
                      quantity: oldLocalInvItem.quantity + int.parse(eventitem.mutation),
                    );
    
                    Amplify.DataStore.save(updatedInventoryItem).then((value) {
                      //update syncMutationLine completed client list
                      List<String> completedList = eventitem.completed ?? [];
                      completedList.add(_clientService.client.id);
                      Amplify.DataStore.save(eventitem.copyWith(completed: completedList));
                    });
                  });
                }
              } catch (e) {
                print('TicketItem was saved, but could not initialise stock into Inventory. ERROR: $e');
              }
            }
          });
        } catch (e) {
          print('Could not transpose inventory line data from syncMutation. ERROR: $e');
        }
      }
    
    
      Future<Inventory> getCorrespondingInventoryLine(String ticketItemID) async {
          double markup = await SharedPreferencesHelper.getBaseMarkup();
    
        return Amplify.DataStore.query(Inventory.classType).then((inventoryList) {
          return Amplify.DataStore.query(TicketItem.classType).then((ticketItemList) {
            TicketItem ti = ticketItemList.firstWhere((tItem) => tItem.id == ticketItemID);
            if (inventoryList.isNotEmpty) {
              return inventoryList.firstWhere((inventoryLine) => inventoryLine.inventoryTicketItemID == ticketItemID,
                  orElse: () {
                    return Inventory(
                        clientid: _clientService.client.id,
                        inventoryclientid: _clientService.client.id,
                        quantity: 0,
                        sellprice: markup * ti.buyprice,
                        inventoryTicketItemID: ticketItemID);
                  });
            } else {
              return Inventory(
                  clientid: _clientService.client.id,
                  inventoryclientid: _clientService.client.id,
                  quantity: 0,
                  sellprice: markup * ti.buyprice,
                  inventoryTicketItemID: ticketItemID);
            }
          });
        });
      }
    }
    

    现在,每当我想调整库存数量(或使用相同逻辑的任何其他表)时,我都会在本地直接保存到 inventory,然后添加一个带有当前设备的 clientID 的 syncedMutations 条目,该条目将生成到所有其他客户端更改相同的信息。像这样:

        Inventory updatedInventoryItem = oldInventoryItem.copyWith( 
       quantity:oldInventoryItem.quantity+valueOfMutation);
        
         
                      int mutationInt = valueOfMutation; // add 10, minus 4 etc
        
                      Amplify.DataStore.save(updatedInventoryItem).then((value) {
                        Amplify.DataStore.save(SyncedMutations(
                            clientid: clientService.client.id,
                            ownerclientid: clientService.actingClient.id,
                            completed: [clientService.client.id],
                            tablename: 'Inventory',
                            itemid: oldInventoryItem.ticketItemID,
                            mutation: mutationInt.toString(),
                            from: oldInventoryItem.quantity.toString(),
                            itemdata: json.encode(updatedInventoryItem.toJson())));
                      });
    

    假设我有两个客户端都有间歇性网络,并且不一定同时打开,每个设备都会在更新发生时更改自己的库存,然后在网络连接时将每个突变操作发送到云中为了找到具有相同clientID 的任何其他设备,syncMutations 然后侦听 create 事件并对其本地(但在线备份)版本的清单进行自己的更新。 然后它还会将自己的clientID 添加到已更新的客户端列表中。

    稍后您可以运行测试以消除所有已完成列表中的每个客户端的所有 syncedMutations。

    我确信有更好或更强大的方法来解决这个问题,但它可以按预期工作,同时将所有逻辑保留在设备上。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-26
      • 2023-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-25
      • 2016-11-02
      相关资源
      最近更新 更多