I have the following classes:
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];
}
And here is what I'm trying to do:
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"
The documentation has the following to say on the subject:
Note that to-many relations are resolved lazily on first access, and then cached in the source entity inside the ToMany object. So subsequent calls to any method, like size() of the ToMany, do not query the database, even if the relation was changed elsewhere. To get the latest data fetch the source entity again or call reset() on the ToMany.
The reset()
method doesn't appear to be available in the Flutter flavor of ObjectBox, and we can see from my example that even fetching both sides of the ToMany
relationship did not result in the expected update.
What am I missing here?
Failed Workaround:
I tried to workaround this problem with the following awful bit of code, but even this does not work. ObjectBox just completely ignores the actual bar.foos
and whatever was persisted for foos
remains there and doesn't get updated.
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);
This makes me think there is something wrong with the way I have the relationship setup, but there are no errors when the ObjectBox generator runs
CALL flutter pub run build_runner build --delete-conflicting-outputs
Update:
I have this working now, but clean it is not. I had my Bar
constructor set up to accept a collection of Foo
objects, but passing the instances of Foo
in is what was causing the relations to break. If I instead create an instance of Bar
, then use bar.foos.add(foo)
, the result is as expected. For what it is worth, that is how the examples in the docs show interactions with relations happening, I just didn't think it was that literal, because creating new objects with relations in this manner is a hassle. I think some work can be done in the constructor to make things a bit easier still.