Fix invalid json deserialization#29
Conversation
|
I understand it, thanks a lot. Seeing your example I see that maybe there are design errors. I think the PocketBaseClient/src/PocketBaseClient/Orm/CollectionBase[T].Repository.cs Lines 134 to 201 in a7a37fa _PocketBaseItemsCount is used to store the number of items in PocketBase server collection. It is set when is known, when PocketBase says to us the real number of items in the collection, then this value is cached in _PocketBaseItemsCount.In line 141 check if all items are cached and there is no need to get items from PocketBase. In the case of having to talk to the server to get the items:
I think that the case of having to talk to the server to get the items should be rethought or redesigned: Step 2 is confusing and maybe it's not necessary. But this is not the cause of the problem you describe, although being a complex process, it affects. About your solution: PERFECT! |
|
I found an issue: And, what happens with fields with multiple values? I do not test it yet: the constructor spects a list of objects, I don't know if json parser deserializes correctly. I will test it. I think your solution is very good |
My proposal is wrong: I didn't take into account that the name of the constructor parameters must match the names of the properties (case insensitive). Adding an |
All item constructors calls the parent constructor with `id`, `created` and `updated` `ItemBase.Id` setter: manages if it must to be registered in the Collection or change an old Id `UpdateWith` do not update if is the same object (checks `ReferenceEquals`)
|
I added some changes:
And relative to my comment:
It works! Parser uses the defined Converters in the Property decorators: Perfect! Again: Thanks!! :) |
About the issue
It's a bit difficult to explain in text. So I made a minimal setup of reproduction and explain what happens behind the code step by step.
Schema of collection
[ { "id": "and4p1eq95kv695", "name": "test1", "type": "base", "system": false, "schema": [ { "id": "txkcgoew", "name": "field", "type": "relation", "system": false, "required": true, "unique": false, "options": { "collectionId": "2lh048rn4ur83kl", "cascadeDelete": false, "maxSelect": 1, "displayFields": [] } } ], "listRule": "", "viewRule": "", "createRule": null, "updateRule": null, "deleteRule": null, "options": {} }, { "id": "2lh048rn4ur83kl", "name": "test2", "type": "base", "system": false, "schema": [ { "id": "p3974g5d", "name": "h", "type": "text", "system": false, "required": true, "unique": false, "options": { "min": null, "max": null, "pattern": "" } }, { "id": "fuqk8cf5", "name": "j", "type": "text", "system": false, "required": true, "unique": false, "options": { "min": null, "max": null, "pattern": "" } } ], "listRule": "", "viewRule": "", "createRule": null, "updateRule": null, "deleteRule": null, "options": {} } ]Records of Test1 collection
{ "page": 1, "perPage": 30, "totalItems": 3, "totalPages": 1, "items": [ { "collectionId": "and4p1eq95kv695", "collectionName": "test1", "created": "2023-02-04 03:50:15.738Z", "field": "h4rvxzuxuf4wom0", "id": "c4qh01f9t23cws4", "updated": "2023-02-04 09:14:32.097Z" }, { "collectionId": "and4p1eq95kv695", "collectionName": "test1", "created": "2023-02-04 03:50:40.302Z", "field": "ugoizglgmoyw2ef", "id": "uevwfoyey6lpr5b", "updated": "2023-02-04 09:14:28.275Z" }, { "collectionId": "and4p1eq95kv695", "collectionName": "test1", "created": "2023-02-04 03:50:46.153Z", "field": "96wi435icppx2rb", "id": "bdgpqv3iwvz3ke4", "updated": "2023-02-04 09:14:23.897Z" } ] }Records of Test2 collection
{ "page": 1, "perPage": 30, "totalItems": 3, "totalPages": 1, "items": [ { "collectionId": "2lh048rn4ur83kl", "collectionName": "test2", "created": "2023-02-04 03:49:52.427Z", "h": "1", "id": "h4rvxzuxuf4wom0", "j": "1", "updated": "2023-02-04 11:47:28.201Z" }, { "collectionId": "2lh048rn4ur83kl", "collectionName": "test2", "created": "2023-02-04 03:49:56.010Z", "h": "2", "id": "ugoizglgmoyw2ef", "j": "2", "updated": "2023-02-04 11:47:25.211Z" }, { "collectionId": "2lh048rn4ur83kl", "collectionName": "test2", "created": "2023-02-04 03:49:58.341Z", "h": "3", "id": "96wi435icppx2rb", "j": "3", "updated": "2023-02-04 11:47:22.547Z" } ] }Reproduction code
Expected outputs and actual outputs are written in
Reproduction code.PocketBaseClient retrieves records of a collection without expanding relation fields. So it creates pseudo objects, which only contains id in it.
In this example,
Test1collection has a relation field ofTest2collection (single records, not multipe records) namedField.NotWorkingProperlymethod inReproduction coderetrieves records of Test1 collection at first. At that time, it does contain a list of pseudo objects inFieldproperty, but does not contain a value ofHandJproperty in each pseudo objects ofFieldproperty. So inside of the foreach statement, a value ofHandJproperty are retrieved at the time of these properties being accessed by getter of it.Getter of ItemBase calls
FillFromPbAsyncat the end.PocketBaseClient/src/PocketBaseClient/Orm/CollectionBase[T].Repository.cs
Lines 110 to 121 in a7a37fa
It updates items correctly, but it does not update
_PocketBaseItemsCountproperty ofCollectionBase<T>class.Let's go back to
Reproduction code. After that, It callsWorkingProperlymethod.WorkingProperlymethod accessesHandJproperty from this time,FirstOrDefault.At the time of calling
FirstOrDefault, Test2Collection callsGetItemsto get enumerator, and it callsGetItemsInternalto retrieves records at the end.PocketBaseClient/src/PocketBaseClient/Orm/CollectionBase[T].Repository.cs
Lines 134 to 201 in a7a37fa
At this time, if statement of line 141 is false since _PocketBaseItemsCount is still null as I said earlier.
So it goes to line 173 and calls
GetPageFromPbAsync.After that, records are not manually added to its collection. It's by design and it seems to be automatically added by internal setter of
idproperty.PocketBaseClient/src/PocketBaseClient/Orm/ItemBase.cs
Lines 26 to 36 in a7a37fa
Collection.ChangeIdInCachecallsUpdateWithat the end.UpdateWithofTest2class, which is generated by pbcodegen is as follows.As you can see, it does not update the reference of records, but it does overwrite each properties of current records in the collection.
But at this time,
item.Hhas its value, butitem.Jdoes not have its value.It's because
Jproperty is deserialized afteridproperty. Let me explain more.Response of PocketBase API is as follows.
{ "page": 1, "perPage": 30, "totalItems": 3, "totalPages": 1, "items": [ { "collectionId": "2lh048rn4ur83kl", "collectionName": "test2", "created": "2023-02-04 03:49:52.427Z", "h": "1", "id": "h4rvxzuxuf4wom0", "j": "1", "updated": "2023-02-04 11:47:28.201Z" }, { "collectionId": "2lh048rn4ur83kl", "collectionName": "test2", "created": "2023-02-04 03:49:56.010Z", "h": "2", "id": "ugoizglgmoyw2ef", "j": "2", "updated": "2023-02-04 11:47:25.211Z" }, { "collectionId": "2lh048rn4ur83kl", "collectionName": "test2", "created": "2023-02-04 03:49:58.341Z", "h": "3", "id": "96wi435icppx2rb", "j": "3", "updated": "2023-02-04 11:47:22.547Z" } ] }As you can see, each properties are lexical ordered. (
collectionId->collectionName->created->h->id->j->updated)System.Text.Json's deserializer goes through the response from top to bottom. So thatJproperty is deserialized right afterIdproperty is being deserialized.Due to this,
item.Hhas its value, butitem.Jdoes not have its value at the time ofUpdateWithbeing called.This is the entire picture of the issue.
I'm so sorry for long explation and I couldn't explain it well. Feel free to ask me if you don't understand.
How I fixed it
The cause of the issue is that collection updates is called at the time of
Idbeing deserialized, not all of the properties being deserialized.So I added constructor for each items by using
[JsonConstructor]to make sure thatUpdateWithis being called after all properties being set.And also, I added a reference equality check in
UpdateWithto avoid miss update. (If reference equals,RemoveAllbreaks impl)