【问题标题】:How to combine two streams where the second depends on the first?如何结合第二个依赖于第一个的两个流?
【发布时间】:2021-09-18 01:22:24
【问题描述】:

我正在构建一个应用,其中 Document 有一个属于 DocumentFamilyDocumentTemplate

我正在处理应用程序的基本 CRUD 操作,我需要一个 watchDocumentTemplateById 方法,在该方法中我得到一个 DocumentTemplate 流,但我还需要查看它的 DocumentFamily

问题是我不知道如何嵌套这两个流,以便当 DocumentFamily 更新时,DocumentTemplate 将拥有更新后的 DocumentFamily

如果有人能告诉我正确的方法,我将不胜感激。 我使用 Flutter 和 ObjectBox 作为我的本地数据库。

这是我开始实现的方法watchDocumentTemplateById(未完成):

@override
Stream<Either<DocumentTemplateFailure, DocumentTemplate>>
      watchDocumentTemplateById(UniqueId templateId) {
    return _box
        .query(DocumentTemplateEntity_.id.equals(templateId.getOrCrash()))
        .watch(triggerImmediately: true)
        .map((query) {
      final templateEntity = query.findFirst();

      if (null != templateEntity) {
        final familyOrFailureStream =
            _documentFamilyRepository.watchDocumentFamilyById(
                UniqueId.fromUniqueString(templateEntity.familyId));

        familyOrFailureStream.listen((familyOrFailure) {
          return familyOrFailure.fold(
            (failure) =>
                DocumentTemplateFailure.familyFailure(familyFailure: failure),
            (family) => templateEntity.toDomain(family: family),
          );
        });
      }
      return left(const DocumentTemplateFailure.notFound());
    });
  }

以及上面提到的类(DocumentTemplate、DocumentTemplateEntity、DocumentFamily 和 DocumentFamilyEntity):

文档模板:

@freezed
class DocumentTemplate with _$DocumentTemplate {
  factory DocumentTemplate({
    required UniqueId id,
    required DocumentTemplateTitle title,
    required double leftMargin,
    required double topMargin,
    required double rightMargin,
    required double bottomMargin,
    required DocumentFamily family,
  }) = _DocumentTemplate;
}

文档模板实体:

@Entity()
class DocumentTemplateEntity {
  @Id()
  int obid = 0;

  @Unique()
  String id;
  String title;
  double leftMargin;
  double topMargin;
  double rightMargin;
  double bottomMargin;
  String familyId;

  DocumentTemplateEntity({
    required this.id,
    required this.title,
    required this.leftMargin,
    required this.topMargin,
    required this.rightMargin,
    required this.bottomMargin,
    required this.familyId,
  });

  DocumentTemplate toDomain({
    required DocumentFamily family,
  }) =>
      DocumentTemplate(
        id: UniqueId.fromUniqueString(id),
        title: DocumentTemplateTitle(title),
        leftMargin: leftMargin,
        topMargin: topMargin,
        rightMargin: rightMargin,
        bottomMargin: bottomMargin,
        family: family,
      );

  factory DocumentTemplateEntity.fromDomain(DocumentTemplate template) =>
      DocumentTemplateEntity(
        id: template.id.getOrCrash(),
        title: template.title.getOrCrash(),
        leftMargin: template.leftMargin,
        topMargin: template.topMargin,
        rightMargin: template.rightMargin,
        bottomMargin: template.bottomMargin,
        familyId: template.family.id.getOrCrash(),
      );
}

文档族:

@freezed
class DocumentFamily with _$DocumentFamily {
  factory DocumentFamily({
    required UniqueId id,
    required int position,
    required String name,
  }) = _DocumentFamily;
}

DocumentFamilyEntity:

@Entity()
class DocumentFamilyEntity {
  @Id()
  int obid = 0;

  @Unique()
  String id;
  String name;
  int position;

  DocumentFamilyEntity({
    required this.id,
    required this.name,
    required this.position,
  });

  DocumentFamily toDomain() => DocumentFamily(
        id: UniqueId.fromUniqueString(id),
        name: name,
        position: position,
      );

  factory DocumentFamilyEntity.fromDomain(DocumentFamily family) =>
      DocumentFamilyEntity(
        id: family.id.getOrCrash(),
        name: family.name,
        position: family.position,
      );
}

提前致谢!

编辑:

我尝试自己解决它并找到了switchMap 方法,它似乎将两个流合二为一,但我不完全确定它是否会起作用,这是更新后的watchDocumentTemplateById 方法:

参考:How do I nest Streams in Dart (map Streams to Stream events)?

  @override
  Stream<Either<DocumentTemplateFailure, DocumentTemplate>>
      watchDocumentTemplateById(UniqueId templateId) {
    return _box
        .query(DocumentTemplateEntity_.id.equals(templateId.getOrCrash()))
        .watch(triggerImmediately: true)
        .switchMap((query) {
      final templateEntity = query.findFirst();
      // if(null == templateEntity) return ??? DocumentTemplateFailure.notFound();
      final familyId = UniqueId.fromUniqueString(templateEntity!.familyId);

      return _documentFamilyRepository.watchDocumentFamilyById(familyId).map(
            (failureOrFamily) => failureOrFamily.fold(
              (failure) => left(
                DocumentTemplateFailure.familyFailure(familyFailure: failure),
              ),
              (family) => right(
                templateEntity.toDomain(family: family),
              ),
            ),
          );
    });
  }

【问题讨论】:

    标签: flutter dart objectbox dart-stream flutter-objectbox


    【解决方案1】:

    您是否考虑过在DocumentFamilyDocumentTemplate 类上直接使用@Entity?请参阅this example,它与包freezed 一起显示。

    在任何情况下,您都可能需要将实体之间的链接表示为 ObjectBox relation 而不仅仅是一个普通的 ID 字段。

    然后 box.query().link() 创建跨实体的查询。如果您 watch() 那个,它会在基础实体以及所有链接实体的更改时触发。

    【讨论】:

    • 这就是我最终所做的。我不知道为什么我想通过嵌套流来做到这一点。
    • 我正在构建一个离线优先的应用程序,其中所有内容都使用 ObjectBox 存储在本地,但我也使用 Supabase 跨设备共享一些用户数据以及我为 Supabase 创建的 DTO将另一个表的 id 作为外键将它们链接在一起(我的 DTO 是与数据库的一对一映射),这可能就是为什么我不想“弄乱”ObjectBox 的特性(如关系),但这实际上是一个使用 ToMany 和 ToOne 注释更容易。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2022-11-16
    相关资源
    最近更新 更多