diff --git a/backend/Directory.Packages.props b/backend/Directory.Packages.props
index ac05ebb181..a9f0614589 100644
--- a/backend/Directory.Packages.props
+++ b/backend/Directory.Packages.props
@@ -109,9 +109,9 @@
-
-
-
+
+
+
diff --git a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs
index 57b4f9f19a..7068c7fa63 100644
--- a/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs
+++ b/backend/FwLite/LcmCrdt.Tests/DataModelSnapshotTests.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using FluentAssertions.Execution;
@@ -74,7 +75,24 @@ private IEnumerable GetPolymorphicTypesFor(Type type)
[Trait("Category", "Verified")]
public async Task VerifyChangeModels()
{
- await Verify(GetPolymorphicTypesFor(typeof(IChange)));
+ await Verify(GetChangePolymorphicTypes());
+ }
+
+ // IChange polymorphism is no longer expressed through PolymorphismOptions; each concrete change
+ // type instead carries a synthetic "$type" property (read by Harmony's change converter), so read
+ // the discriminator from there to snapshot the registered change set.
+ private IEnumerable GetChangePolymorphicTypes()
+ {
+ return LcmCrdtKernel.AllChangeTypes()
+ .Select(changeType =>
+ {
+ var discriminatorProperty = _jsonSerializerOptions.GetTypeInfo(changeType).Properties
+ .SingleOrDefault(p => p.Name == CrdtConstants.ChangeDiscriminatorProperty);
+ discriminatorProperty.Should().NotBeNull("change type {0} should declare a $type discriminator", changeType);
+ var discriminator = discriminatorProperty!.Get!(RuntimeHelpers.GetUninitializedObject(changeType));
+ return new JsonDerivedType(changeType, (string)discriminator!);
+ })
+ .OrderBy(t => t.DerivedType.FullName);
}
[Fact]
diff --git a/backend/FwLite/LcmCrdt.Tests/TestJsonOptions.cs b/backend/FwLite/LcmCrdt.Tests/TestJsonOptions.cs
index 76bc8ad5a3..8f7191be71 100644
--- a/backend/FwLite/LcmCrdt.Tests/TestJsonOptions.cs
+++ b/backend/FwLite/LcmCrdt.Tests/TestJsonOptions.cs
@@ -18,6 +18,7 @@ public static JsonSerializerOptions Harmony()
{
var config = new CrdtConfig();
LcmCrdtKernel.ConfigureCrdt(config);
+ LcmCrdtKernel.FinalizeJsonSerializerOptions(config);
return config.JsonSerializerOptions;
}
}
diff --git a/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs b/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
index 65b2863e37..ccdb14d0b8 100644
--- a/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
+++ b/backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
@@ -66,6 +66,9 @@ public static IServiceCollection AddLcmCrdtClientCore(this IServiceCollection se
config => ConfigureCrdt(config, false)//don't add remote resources because they are added in AddCrdtRemoteResources
);
services.AddCrdtRemoteResources();
+ // AddCrdtRemoteResources registers its change types in a Configure callback, so finalize the
+ // JSON options in a PostConfigure (which runs after every Configure) — see FinalizeJsonSerializerOptions.
+ services.AddOptions().PostConfigure(FinalizeJsonSerializerOptions);
services.AddOptions().PostConfigure((CrdtConfig crdtConfig, IOptions lcmConfig) =>
{
crdtConfig.LocalResourceCachePath = Path.Combine(lcmConfig.Value.ProjectPath, "localResourcesCache");
@@ -395,12 +398,21 @@ public static void ConfigureCrdt(CrdtConfig config, bool addRemoteResourceEntity
// you must add an instance of it to UseChangesTests.GetAllChanges()
;
+ if (addRemoteResourceEntity)
+ config.AddRemoteResourceEntity();
+ }
+
+ // Adds the legacy ExampleSentence.Translation modifier to Harmony's JSON options. Reading
+ // config.JsonSerializerOptions builds the lazily-created options and freezes ChangeTypeListBuilder,
+ // so this must run only once every change type is registered. It's therefore kept out of
+ // ConfigureCrdt and run once the config is complete (a PostConfigure in DI, see AddLcmCrdtClientCore;
+ // right before reading the options for standalone callers, see TestJsonOptions.Harmony).
+ // Call once per config: it wraps the existing resolver, so a second call double-adds the modifier.
+ internal static void FinalizeJsonSerializerOptions(CrdtConfig config)
+ {
config.JsonSerializerOptions.TypeInfoResolver =
(config.JsonSerializerOptions.TypeInfoResolver ?? new DefaultJsonTypeInfoResolver())
.WithAddedModifier(Json.ExampleSentenceTranslationModifier);
-
- if (addRemoteResourceEntity)
- config.AddRemoteResourceEntity();
}
public static IEnumerable AllChangeTypes()