danielgerlag / danielgerlag/workflow-core
Deserialisation in the ToWorkflowInstance() duplicates List items due to not set ObjectCreationHandling = ObjectCreationHandling.Replace in the JsonSerializerSettings
- Dominant language
- C#
- Stars
- 5.9k
- Forks
- 1.3k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 5
Description
Hello. I was trying out the Workflow Core library recently and found a bug in the deserialisation in the [ToWorkflowInstance()](https://github.com/danielgerlag/workflow-core/blob/a761d783fe6f6bf9b0c6797321a439bf6f94c827/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs#L136) method when using SqlServer as the persistence (which uses the EntityFrameworkPersistenceProvider under the hood).
Basically,
`result.Data = JsonConvert.DeserializeObject(instance.Data, SerializerSettings);`
is duplicating List items due a `JsonSerializerSetting`, `ObjectCreationHandling = ObjectCreationHandling.Replace` not set. This in turn, causes all workflow steps to duplicate the items in lists.
More about the `ObjectCreationHandling` setting [here](https://www.newtonsoft.com/json/help/html/DeserializeObjectCreationHandling.htm) and it's the same problem as described [here](https://stackoverflow.com/questions/13394401/json-net-deserializing-list-gives-duplicate-items).
To reproduce:
1. [you need to configure your own SQL persistance](https://github.com/danielgerlag/workflow-core/tree/master/src/providers/WorkflowCore.Persistence.SqlServer)
2. then go into [ToWorkflowInstance()](https://github.com/danielgerlag/workflow-core/blob/a761d783fe6f6bf9b0c6797321a439bf6f94c827/src/providers/WorkflowCore.Persistence.EntityFramework/ExtensionMethods.cs#L136) and before deserialising assign `instance.Data` some sample json which contains a list with items, you can use mine, ex.
```
instance.Data = "{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestData, AtlasWorkflowEngine.Services\",\"ApprovalStages\":{\"$type\":\"System.Collections.Generic.List`1[[AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services]], System.Private.CoreLib\",\"$values\":[{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services\",\"StageOrder\":1,\"StageName\":\"Stage 1.1\",\"ApprovedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\"]},\"RejectedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"ResponseTimes\":{\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.DateTime, System.Private.CoreLib]], System.Private.CoreLib\"},\"RequiredApprovers\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\",\"test2@test.com\"]},\"ApprovalEventId\":\"4c3d8dbb-f0f1-4b9f-b4a8-39d70f213c69\",\"RequireAllApprovers\":true,\"StageIsApproved\":false,\"StageIsRejected\":false},{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services\",\"StageOrder\":1,\"StageName\":\"Stage 1.2\",\"ApprovedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"RejectedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"ResponseTimes\":{\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.DateTime, System.Private.CoreLib]], System.Private.CoreLib\"},\"RequiredApprovers\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\",\"test2@test.com\",\"test3@test.com\"]},\"ApprovalEventId\":\"acb565b7-26cc-47d8-ad54-c0dbcbbe64e8\",\"RequireAllApprovers\":false,\"StageIsApproved\":false,\"StageIsRejected\":false},{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services\",\"StageOrder\":2,\"StageName\":\"Stage 2\",\"ApprovedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"RejectedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"ResponseTimes\":{\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.DateTime, System.Private.CoreLib]], System.Private.CoreLib\"},\"RequiredApprovers\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\"]},\"ApprovalEventId\":\"4e28761f-2544-4f64-9f88-38278c2828ed\",\"RequireAllApprovers\":true,\"StageIsApproved\":false,\"StageIsRejected\":false}]},\"CurrentStage\":{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services\",\"StageOrder\":1,\"StageName\":\"Stage 1.1\",\"ApprovedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\"]},\"RejectedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"ResponseTimes\":{\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.DateTime, System.Private.CoreLib]], System.Private.CoreLib\"},\"RequiredApprovers\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\",\"test2@test.com\"]},\"ApprovalEventId\":\"4c3d8dbb-f0f1-4b9f-b4a8-39d70f213c69\",\"RequireAllApprovers\":true,\"StageIsApproved\":false,\"StageIsRejected\":false},\"CurrentStages\":{\"$type\":\"System.Collections.Generic.List`1[[AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services]], System.Private.CoreLib\",\"$values\":[{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services\",\"StageOrder\":1,\"StageName\":\"Stage 1.1\",\"ApprovedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\"]},\"RejectedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"ResponseTimes\":{\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.DateTime, System.Private.CoreLib]], System.Private.CoreLib\"},\"RequiredApprovers\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\",\"test2@test.com\"]},\"ApprovalEventId\":\"4c3d8dbb-f0f1-4b9f-b4a8-39d70f213c69\",\"RequireAllApprovers\":true,\"StageIsApproved\":false,\"StageIsRejected\":false},{\"$type\":\"AtlasWorkflowEngine.Services.Workflows.TestWorkflow.TestStage, AtlasWorkflowEngine.Services\",\"StageOrder\":1,\"StageName\":\"Stage 1.2\",\"ApprovedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"RejectedBy\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[]},\"ResponseTimes\":{\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.DateTime, System.Private.CoreLib]], System.Private.CoreLib\"},\"RequiredApprovers\":{\"$type\":\"System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib\",\"$values\":[\"test1@test.com\",\"test2@test.com\",\"test3@test.com\"]},\"ApprovalEventId\":\"acb565b7-26cc-47d8-ad54-c0dbcbbe64e8\",\"RequireAllApprovers\":false,\"StageIsApproved\":false,\"StageIsRejected\":false}]}}";
```
3. Step over and you will see the duplicated items in lists. If you use the json I provided above, the expected count for the ApprovedBy is 1 and RequiredApprovers 2, however the counts are getting doubled each time the `ToWorkflowInstance()` is being called.

I believe the fix is to simply set the `ObjectCreationHandling = ObjectCreationHandling.Replace` like this:
```csharp
private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All,
ObjectCreationHandling = ObjectCreationHandling.Replace
};
```
but as I have a very limited knowledge and understanding of this library I kindly ask for your assistance in evaluating the impacts and/or possible side effects this might have on the rest of the library.
I'm happy to create a pull request for this "bugfix" and cross reference with this thread this afternoon.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.