diff --git a/src/JsonPatchGenerator/DefaultJsonPatchGenerator.cs b/src/JsonPatchGenerator/DefaultJsonPatchGenerator.cs index 4d0860c..c020b7e 100644 --- a/src/JsonPatchGenerator/DefaultJsonPatchGenerator.cs +++ b/src/JsonPatchGenerator/DefaultJsonPatchGenerator.cs @@ -11,12 +11,12 @@ namespace Firebend.JsonPatch; public class DefaultJsonPatchGenerator : IJsonPatchGenerator { private readonly IJsonDiffDetector _diffDetector; - private readonly IJsonPatchWriter _writer; + private readonly Func _writerFactory; private readonly IJsonDiffSettingsProvider _settings; - public DefaultJsonPatchGenerator(IJsonDiffDetector diffDetector, IJsonPatchWriter writer, IJsonDiffSettingsProvider settings) + public DefaultJsonPatchGenerator(IJsonDiffDetector diffDetector, Func writerFactory, IJsonDiffSettingsProvider settings) { _diffDetector = diffDetector; - _writer = writer; + _writerFactory = writerFactory; _settings = settings; } @@ -40,6 +40,10 @@ public JsonPatchDocument Generate(T original, T modified) var diffs = _diffDetector.DetectChanges(original, modified); + if (!diffs.Any()) { return new(); } + + var writer = _writerFactory(); + foreach (var jsonDiff in diffs) { switch (jsonDiff.Change) @@ -47,20 +51,20 @@ public JsonPatchDocument Generate(T original, T modified) case JsonChange.Unknown: break; case JsonChange.Add: - _writer.WriteAdd(jsonDiff.Path, jsonDiff.Value); + writer.WriteAdd(jsonDiff.Path, jsonDiff.Value); break; case JsonChange.Replace: - _writer.WriteReplace(jsonDiff.Path, jsonDiff.Value); + writer.WriteReplace(jsonDiff.Path, jsonDiff.Value); break; case JsonChange.Remove: - _writer.WriteRemove(jsonDiff.Path); + writer.WriteRemove(jsonDiff.Path); break; default: throw new ArgumentOutOfRangeException(); } } - var patchJson = _writer.Finish(); + var patchJson = writer.Finish(); if (string.IsNullOrWhiteSpace(patchJson)) { diff --git a/src/JsonPatchGenerator/Extensions/ServiceCollectionExtensions.cs b/src/JsonPatchGenerator/Extensions/ServiceCollectionExtensions.cs index ac6032f..1161260 100644 --- a/src/JsonPatchGenerator/Extensions/ServiceCollectionExtensions.cs +++ b/src/JsonPatchGenerator/Extensions/ServiceCollectionExtensions.cs @@ -46,7 +46,10 @@ public static IServiceCollection AddJsonPatchGenerator(this IServiceCollection c collection.TryAddSingleton(new JsonDiffSettingsProvider(settings)); collection.TryAddTransient(); - collection.TryAddTransient(); + collection.TryAddTransient(sp => + new DefaultJsonPatchGenerator(sp.GetRequiredService(), + sp.GetRequiredService, + sp.GetRequiredService())); collection.TryAddTransient(); return collection; diff --git a/tests/JsonPatchGenerator.Tests/JsonPatchGeneratorTests.cs b/tests/JsonPatchGenerator.Tests/JsonPatchGeneratorTests.cs index d500663..c21ec7f 100644 --- a/tests/JsonPatchGenerator.Tests/JsonPatchGeneratorTests.cs +++ b/tests/JsonPatchGenerator.Tests/JsonPatchGeneratorTests.cs @@ -12,6 +12,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; +using System.Threading.Tasks; // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -620,6 +621,41 @@ public void Json_Patch_Generator_Should_Handle_Multiple_Generations() patchWithNoChanges.Operations.Should().BeEmpty(); } + [TestMethod] + public async Task Json_Patch_Document_Generator_Should_Be_Thread_Safe() + { + //arrange + var generator = CreateGenerator(); + + var a1 = new Agent { FirstName = "A" }; + var b1 = new Agent { FirstName = "B" }; + + var a2 = new Agent { LastName = "C" }; + var b2 = new Agent { LastName = "D" }; + + //act + var task1 = Task.Run(() => generator.Generate(a1, b1)); + var task2 = Task.Run(() => generator.Generate(a2, b2)); + + await Task.WhenAll(task1, task2); + + //assert + var patch1 = await task1; + var patch2 = await task2; + + patch1.Operations.Should().HaveCount(1); + var op1 = patch1.Operations[0]; + op1.op.Should().Be("replace"); + op1.path.Should().Be("/FirstName"); + op1.value.Should().Be("B"); + + patch2.Operations.Should().HaveCount(1); + var op2 = patch2.Operations[0]; + op2.op.Should().Be("replace"); + op2.path.Should().Be("/LastName"); + op2.value.Should().Be("D"); + } + private static JsonSerializerSettings CreateCustomSettings() { var serializerSettings = new JsonSerializerSettings