Fix hydration of proxy objects with lazy public properties #1784
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This pull request fixes #1775 by ensuring the proxy initializer is unset before hydration for a proxy document begins. As shown in the provided test case, this can lead to data loss if
a) a document contains public properties and is still an uninitialized proxy
b) the document contains a
many
relationship (reference or embed) which isn't changed during the process.The data loss occurs because the hydrator sets the value of a lazy public property, triggering proxy initialization. For one, this causes an additional database read which can cause performance problems.
Setting aside any issues due to data being changed in the database in the meantime, the document in the end will be exactly what to expect, without any issues. However, after proxy initialization is complete, the original hydration cycle for the document sets the
originalValue
for the document in UnitOfWork. This sets a differentPersistentCollection
(containing the same data internally) asoriginalValue
for anymany
relationship.The next time a change set is computed for the (initialized) proxy document, UnitOfWork compares
originalValue
andactualValue
for the relationship. Since the instance changed, it schedules a collection deletion for theoriginalValue
. However, sinceactualValue
already is aPersistentCollection
and isn't marked asdirty
(because we didn't change anything in the collection), it won't be persisted, leaving only the collection deletion to cause some data loss.The root issue is fixed by the changes in
HydratorFactory
, which ensures that a proxy object with public properties is not hydrated twice, removing the cause for the issue. I checked to see whether we need to apply some changes to the logic which deals with collection replacements, but I figured out that the only way we could end up with a non-dirty persistent collection inactualValue
is ifa) a non-empty collection was replaced with an empty one; in this case the deletion without insertion is the correct course of action
b) a non-dirty persistent collection containing data was injected into a document; in this case, we won't prevent a user from shooting themselves in the foot.