diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd68f4..8704dbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] + +## [1.12.4] - 2024-09-05 + +### Changed + +- Improves performance of the InMemoryBackingStore when reading properties. [#347](https://github.com/microsoft/kiota-dotnet/issues/347) + ## [1.12.3] - 2024-09-03 ### Changed diff --git a/Directory.Build.props b/Directory.Build.props index 1e5ce4e..f202c97 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 1.12.3 + 1.12.4 false diff --git a/src/abstractions/store/InMemoryBackingStore.cs b/src/abstractions/store/InMemoryBackingStore.cs index 4d50c28..d845338 100644 --- a/src/abstractions/store/InMemoryBackingStore.cs +++ b/src/abstractions/store/InMemoryBackingStore.cs @@ -15,10 +15,21 @@ namespace Microsoft.Kiota.Abstractions.Store public class InMemoryBackingStore : IBackingStore { private bool isInitializationComplete = true; + private bool _returnOnlyChangedValues = false; + /// /// Determines whether the backing store should only return changed values when queried. /// - public bool ReturnOnlyChangedValues { get; set; } + public bool ReturnOnlyChangedValues + { + get => _returnOnlyChangedValues; + + set + { + _returnOnlyChangedValues = value; + ForwardReturnOnlyChangedValuesToNestedInstances(); + } + } private readonly ConcurrentDictionary> store = new(); private readonly ConcurrentDictionary> subscriptions = new(); @@ -34,7 +45,10 @@ public class InMemoryBackingStore : IBackingStore if(store.TryGetValue(key, out var result)) { - EnsureCollectionPropertyIsConsistent(key, result.Item2); + if(ReturnOnlyChangedValues) + { + EnsureCollectionPropertyIsConsistent(key, result.Item2); + } var resultObject = result.Item2; if(resultObject is Tuple collectionTuple) { @@ -128,6 +142,14 @@ public void Set(string key, T? value) /// A collection of strings containing keys changed to null public IEnumerable EnumerateKeysForValuesChangedToNull() { + if(ReturnOnlyChangedValues) // refresh the state of collection properties if they've changed in size. + { + foreach(var item in store) + { + EnsureCollectionPropertyIsConsistent(item.Key, item.Value.Item2); + } + } + foreach(var item in store) { if(item.Value.Item1 && item.Value.Item2 == null) @@ -228,5 +250,24 @@ private void EnsureCollectionPropertyIsConsistent(string key, object? storeItem) } } } + + private void ForwardReturnOnlyChangedValuesToNestedInstances() + { + foreach(var item in store.Values) + { + if(item.Item2 is Tuple collectionTuple) + { + foreach(var collectionItem in collectionTuple.Item1) + { + if(collectionItem is not IBackedModel backedModel) break; + backedModel.BackingStore.ReturnOnlyChangedValues = _returnOnlyChangedValues; + } + } + else if(item.Item2 is IBackedModel backedModel) + { + backedModel.BackingStore.ReturnOnlyChangedValues = _returnOnlyChangedValues; + } + } + } } } diff --git a/tests/abstractions/Store/InMemoryBackingStoreTests.cs b/tests/abstractions/Store/InMemoryBackingStoreTests.cs index 8e5932e..2f393f3 100644 --- a/tests/abstractions/Store/InMemoryBackingStoreTests.cs +++ b/tests/abstractions/Store/InMemoryBackingStoreTests.cs @@ -487,6 +487,44 @@ public void TestsLargeArrayPerformsWell() Assert.InRange(stopWatch.ElapsedMilliseconds, 0, 1); } + [Fact] + public void TestsLargeObjectReadPerformsWell() + { + // Arrange dummy user with many child objects + var testUser = new TestEntity + { + Id = "84c747c1-d2c0-410d-ba50-fc23e0b4abbe", + Colleagues = Enumerable.Range(1, 100) + .Select(_ => new TestEntity + { + Id = Guid.NewGuid().ToString(), + BusinessPhones = new List + { + "+1 234 567 891" + }, + Colleagues = Enumerable.Range(1, 100) + .Select(_ => new TestEntity + { + Id = Guid.NewGuid().ToString(), + BusinessPhones = new List + { + "+1 234 567 891" + } + }) + .ToList() + }) + .ToList() + }; + + // Act on the data by reading a property + var stopWatch = Stopwatch.StartNew(); + _ = testUser.Colleagues[0]; + stopWatch.Stop(); + + // Assert + Assert.InRange(stopWatch.ElapsedMilliseconds, 0, 1); + } + /// /// Helper function to pull out the private `subscriptions` collection property from the InMemoryBackingStore class ///