diff --git a/src/abstractions/extensions/IBackedModelExtensions.cs b/src/abstractions/extensions/IBackedModelExtensions.cs
new file mode 100644
index 00000000..39ce3aa9
--- /dev/null
+++ b/src/abstractions/extensions/IBackedModelExtensions.cs
@@ -0,0 +1,24 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
+// ------------------------------------------------------------------------------
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Microsoft.Kiota.Abstractions.Store;
+
+namespace Microsoft.Kiota.Abstractions.Extensions
+{
+ ///
+ /// Extension methods for instances.
+ ///
+ public static class IBackedModelExtensions
+ {
+ ///
+ /// Sets all fields recursively to "modified" so they will be sent in the next serialization.
+ /// This is useful to allow the model object to be reused to send to a POST or PUT call.
+ /// Do not use if you are using a sparse PATCH.
+ ///
+ public static void MakeSendable(this IBackedModel kiotaModelObject) => kiotaModelObject.BackingStore.MakeSendable();
+ }
+}
diff --git a/src/abstractions/store/IBackingStore.cs b/src/abstractions/store/IBackingStore.cs
index 487658cc..46760681 100644
--- a/src/abstractions/store/IBackingStore.cs
+++ b/src/abstractions/store/IBackingStore.cs
@@ -56,5 +56,11 @@ public interface IBackingStore
bool InitializationCompleted { get; set; }
/// Whether to return only values that have changed since the initialization of the object when calling the Get and Enumerate methods.
bool ReturnOnlyChangedValues { get; set; }
+ ///
+ /// Sets all fields recursively to "modified" so they will be sent in the next serialization.
+ /// This is useful to allow the model object to be reused to send to a POST or PUT call.
+ /// Do not use if you are using a sparse PATCH.
+ ///
+ void MakeSendable();
}
}
diff --git a/src/abstractions/store/InMemoryBackingStore.cs b/src/abstractions/store/InMemoryBackingStore.cs
index d8453388..e5d0ad0d 100644
--- a/src/abstractions/store/InMemoryBackingStore.cs
+++ b/src/abstractions/store/InMemoryBackingStore.cs
@@ -220,6 +220,38 @@ public bool InitializationCompleted
}
}
+ ///
+ /// Sets all fields recursively to "modified" so they will be sent in the next serialization.
+ /// This is useful to allow the model object to be reused to send to a POST or PUT call.
+ /// Do not use if you are using a sparse PATCH.
+ ///
+ public void MakeSendable()
+ {
+ ReturnOnlyChangedValues = false;
+
+ foreach(var entry in store)
+ {
+ store[entry.Key] = Tuple.Create(true, entry.Value.Item2);
+
+ if(entry.Value.Item2 is Tuple collectionTuple)
+ {
+ foreach(var collectionItem in collectionTuple.Item1)
+ {
+ if(collectionItem is not IBackedModel backedModel)
+ {
+ break;
+ }
+
+ backedModel.BackingStore.MakeSendable();
+ }
+ }
+ else if(entry.Value.Item2 is IBackedModel backedModel)
+ {
+ backedModel.BackingStore.MakeSendable();
+ }
+ }
+ }
+
private void EnsureCollectionPropertyIsConsistent(string key, object? storeItem)
{
if(storeItem is Tuple collectionTuple) // check if we put in a collection annotated with the size
diff --git a/tests/abstractions/Store/InMemoryBackingStoreTests.cs b/tests/abstractions/Store/InMemoryBackingStoreTests.cs
index 2f393f34..a6963803 100644
--- a/tests/abstractions/Store/InMemoryBackingStoreTests.cs
+++ b/tests/abstractions/Store/InMemoryBackingStoreTests.cs
@@ -1,9 +1,7 @@
-using System;
using System.Collections;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.Linq;
using System.Reflection;
+using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions.Store;
using Microsoft.Kiota.Abstractions.Tests.Mocks;
@@ -80,7 +78,7 @@ public void TestsBackingStoreEmbeddedInModel()
Assert.Equal("businessPhones", changedValues.First().Key);
}
[Fact]
- public void TestsBackingStoreEmbeddedInModelWithAdditionDataValues()
+ public void TestsBackingStoreEmbeddedInModelWithAdditionalDataValues()
{
// Arrange dummy user with initialized backingstore
var testUser = new TestEntity
@@ -443,6 +441,53 @@ public void TestsBackingStoreEmbeddedInModelWithByUpdatingNestedIBackedModelColl
Assert.Single(colleagueSubscriptions);// only one subscription to be invoked for the collection "colleagues"
}
+ [Fact]
+ public void TestsMakeRunnableOnMultipleNestingAndCollectionPatterns()
+ {
+ var testUser = new TestEntity
+ {
+ Id = "84c747c1-d2c0-410d-ba50-fc23e0b4abbe",
+ Manager = new TestEntity
+ {
+ Id = "1a1e218d-1a85-450a-b96e-ab0786b9022b"
+ },
+ Colleagues =
+ [
+ new TestEntity
+ {
+ Id = "2fe22fe5-1132-42cf-90f9-1dc17e325a74",
+ BusinessPhones = [ "+1 234 567 891" ]
+ }
+ ]
+ };
+ testUser.BackingStore.InitializationCompleted = testUser.Colleagues[0].BackingStore.InitializationCompleted = testUser.Manager.BackingStore.InitializationCompleted = true;
+
+ // Simulate a serialization. Verify that the model serializes to an empty result since all existing data values are marked as "not changed".
+ testUser.Manager.BackingStore.ReturnOnlyChangedValues = true;
+ testUser.Colleagues[0].BackingStore.ReturnOnlyChangedValues = true; //serializer will do this.
+ testUser.BackingStore.ReturnOnlyChangedValues = true;
+ var changedValues = testUser.BackingStore.Enumerate().ToDictionary(x => x.Key, y => y.Value!);
+ Assert.Empty(changedValues);
+
+ // Make the top-level entity sendable and verify that the resulting setting of "changed" on
+ // every recursive IBackedModel and collection results in returning all objects in the result.
+ testUser.MakeSendable();
+ changedValues = testUser.BackingStore.Enumerate().ToDictionary(x => x.Key, y => y.Value!);
+ Assert.NotEmpty(changedValues);
+ Assert.True(changedValues.TryGetValue("id", out var idObj));
+ Assert.Equal("84c747c1-d2c0-410d-ba50-fc23e0b4abbe", idObj);
+ Assert.True(changedValues.TryGetValue("manager", out var managerObj));
+ var manager = (TestEntity)managerObj;
+ Assert.Equal("1a1e218d-1a85-450a-b96e-ab0786b9022b", manager.Id);
+ Assert.True(changedValues.ContainsKey("colleagues"));
+ var colleagues = ((Tuple)changedValues["colleagues"]).Item1.Cast().ToList();
+ Assert.Single(colleagues);
+ Assert.Equal("2fe22fe5-1132-42cf-90f9-1dc17e325a74", colleagues[0].Id);
+ Assert.NotNull(colleagues[0].BusinessPhones);
+ Assert.Single(colleagues[0].BusinessPhones!);
+ Assert.Equal("+1 234 567 891", colleagues[0].BusinessPhones![0]);
+ }
+
[Fact]
public void TestsBackingStoreNestedInvocationCounts()
{