使用Equitable和ObjectBox的Flutter:ToMany关系未正确更新

6kkfgxo0  于 2023-01-31  发布在  Flutter
关注(0)|答案(1)|浏览(146)

我有以下课程:

import 'package:equatable/equatable.dart';
import 'package:objectbox/objectbox.dart';

@Entity()
/*
All fields of a class which extends Equatable should be immutable, but ObjectBox
requires the `id` field to be mutable because its value is set after an instance of
the class has been created.  Because of this, we ignore the linter rule
"must_be_immutable" on all ObjectBox entities.
*/
// ignore: must_be_immutable
class Foo extends Equatable {
  int id;
  final String fooProp;

  // I don't need a backlink yet, but very likely will in the future
  // @Backlink()
  // final ToMany<Bar> bars;

  Foo(
    this.fooProp,
    {
      this.id=0,
    }
  );

  @override
  List<Object> get props => [fooProp];
}
import 'package:equatable/equatable.dart';
import 'package:objectbox/objectbox.dart';

@Entity()
/*
All fields of a class which extends Equatable should be immutable, but ObjectBox
requires the `id` field to be mutable because its value is set after an instance of
the class has been created.  Because of this, we ignore the linter rule
"must_be_immutable" on all ObjectBox entities.
*/
// ignore: must_be_immutable
class Bar extends Equatable {
  int id;
  final String barProp;
  final ToMany<Foo> foos;

  Bar(
    this.barProp,
    this.foos,
    {
      this.id=0,
    }
  );

  @override
  List<Object> get props => [barProp, foos];
}

这就是我要做的

import 'package:foo_bar/objectbox/objectbox.dart';

// Get previously stored instance of Foo
Foo foo = ObjectBox.fooBox.get(1);

// Print foo.fooProp
print(foo.fooProp);  // Prints "asdf"

// Change foo.fooProp to something else
foo.fooProp = 'fdsa';

// Update foo
ObjectBox.fooBox.put(foo);

// Get the same instance of Foo again
foo = ObjectBox.fooBox.get(1);

// Check foo.fooProp to make sure it updated
print(foo.fooProp);  // Prints "fdsa", good so far

// Get previously stored instance of Bar which has Foo instance with ID of 1 in its foos
Bar bar = ObjectBox.barBox.get(1);

// Get our foo from bar.foos
foo = bar.foos[0];

// Verify the ID of foo to make sure it is the same object
print(foo.id);  // Prints "1", exactly what we expect

// Print foo.fooProp
print(foo.fooProp); // Prints "asdf", not the expected "fdsa"

该文件对这一问题作了如下说明:
请注意,to-many关系在第一次访问时被延迟解析,然后缓存在ToMany对象内的源实体中。因此,对任何方法(如ToMany对象的size())的后续调用都不会查询数据库,即使关系在其他地方发生了更改。要获取最新数据,请再次获取源实体或在ToMany对象上调用reset()。
reset()方法在ObjectBox的Flutter风格中似乎不可用,并且我们可以从我的示例中看到,即使获取ToMany关系的双方也不会导致预期的更新。
我错过了什么?

    • 失败的解决方法:**

我试着用下面这段糟糕的代码来解决这个问题,但即使这样也不起作用。ObjectBox完全忽略了实际的bar.foos,并且为foos持久化的内容仍然存在,不会更新。

final List<Bar> oldBars = ObjectBox.barBox.getAll();
List<Bar> newBars = [];
for(Bar oldBar in oldBars) {
  if(oldBar.foos.isNotEmpty) {
    List<int> oldFooIds = oldBar.foos.map((foo) => foo.id).toList();
    List<Foo> newFoos = foos.where((foo) => oldFooIds.contains(foo.id)).toList();
    Bar newBar = oldBar.copy(foos: ToMany<Foo>(items: newFoos));
    newBars.add(newBar);
  }
}

ObjectBox.barBox.putMany(newBars);

这让我觉得我的关系设置方式有问题,但是当ObjectBox生成器运行时没有错误

CALL flutter pub run build_runner build --delete-conflicting-outputs
    • 更新日期:**

我现在可以工作了,但是没有清理它。我设置了Bar构造函数来接受Foo对象的集合,但是传入Foo的示例是导致关系中断的原因。如果我创建Bar的示例,然后使用bar.foos.add(foo),则结果与预期的一样。无论如何,这就是文档中的例子如何显示与关系的交互,我只是不认为它是字面上的,因为用这种方式创建新的对象是一件麻烦的事。我认为可以在构造函数中做一些工作,使事情变得更容易。

wnavrhmk

wnavrhmk1#

这个问题的一个“解决方案”(我不严格地使用这个术语)是对Bar类进行如下修改,这允许我使用预先构建的x1m2 n1 a示例列表来初始化Bar示例。

import 'package:equatable/equatable.dart';
import 'package:objectbox/objectbox.dart';

@Entity()
/*
All fields of a class which extends Equatable should be immutable, but ObjectBox
requires the `id` field to be mutable because its value is set after an instance of
the class has been created.  Because of this, we ignore the linter rule
"must_be_immutable" on all ObjectBox entities.
*/
// ignore: must_be_immutable
class Bar extends Equatable {
  int id;
  final String barProp;
  final ToMany<Foo> foos = ToMany<Foo>();

  Bar(
    this.barProp,
    {
      this.id=0,
      foos=const <Foo>[],
    }
  ) {
    this.foos.addAll(foos);
  }

  @override
  List<Object> get props => [barProp, foos];
}

对于创建新对象来说,这很好用,但是因为我想使用Equatable,所以我必须使所有用于确定相等性的属性都是final。当一个类是一个@Entity,它将持久化到ObjectBox中时,它的大多数属性通常将用于确定相等性。所以Equatable的这个需求使得它在更新对象时与ObjectBox不一致,例如,如果我有一个Bar的示例,我不能更新barProp;我必须创建一个Bar的新示例,它将barProp初始化为所需的值。如果我创建一个Bar的新示例,它具有barProp所需的值foos,并且具有与Bar的已持久化示例相同的ID,那么我将尝试持久化该新示例。barProp将按预期进行更新,但foos不会。要说的是,在持久化Bar的新示例之前,我首先必须采取调用ObjectBox.barBox.remove()ObjectBox.barBox.removeAll()(取决于应用程序)的繁重方法。

Foo fooA = Foo('this is Foo A');
Foo fooB = Foo('this is Foo B');
List<Foo> firstFooList = <Foo>[fooA, fooB];
ObjectBox.fooBox.putMany(firstFooList);

Foo fooC = Foo('this is Foo C')
Foo fooD = Foo('this is Foo D')
List<Foo> secondFooList = <Foo>[fooC, fooD];
ObjectBox.fooBox.putMany(secondFooList);

Bar barA = Bar('this is bar A', foos=firstFooList)
ObjectBox.barBox.put(barA);  // ObjectBox assigns ID 1 to this Bar

Bar barB = Bar('this is bar B', id=barA.id, foos=secondFooList)  // Use the `id` from barA which we just persisted, but initialize `foos` with `secondFooList`

// Without this, the next line which persists `barB` would result in the
// instance of Bar which has ID 1 having a `barProp` value of 'this is bar B', 
// but a `foos` value equal to `firstFooList`, not the expected `secondFooList`.
ObjectBox.barBox.remove(barB.id);

ObjectBox.barBox.put(barB);

时间会证明像这样调用removeremoveAll是否比不使用Equatable对性能造成更大的影响,对于阅读本文的其他人来说,这种判断将取决于您的特定应用程序(例如,由于包含Equatable,您的应用是否有更多的UI交互可受益于构建版本的减少,或者您的应用是否有更多ObjectBox交互,过多调用removeremoveAll会导致性能下降)。

相关问题