Runtime plugin pattern: KnownSubTypeOf, RegisterSubtypeAssembly, RegisterDynamicSubtype - #211
Open
manuc66 wants to merge 5 commits into
Open
Conversation
…ypeResolution attribute
JsonSubTypesTypeResolution.AddAssembly was a process-wide mutable registry that
leaked state across serialization profiles. Replace it with
[JsonSubTypesTypeResolution("AssemblyName")] on the base type, cached per type.
The attribute takes an assembly name rather than a Type: a Type would force the
base to reference the plugin assembly, which together with the plugin's reference
to the base creates a project cycle. Naming the assembly keeps the base free of a
compile-time reference to the plugin.
The cross-assembly tests now use SharedAnimal (base) with the plugin assembly
declared on it and PluginDog (in the plugin project) resolved by name, plus a
negative test on a base without the attribute.
The attribute's previous name was inherited from the global AddAssembly registry it replaced and said nothing about its contract. KnownSubTypeOtherAssembly is a noun consistent with the KnownSubType family, and Other carries the extension beyond the base type's own assembly. The file follows the class name.
…SubtypeAssembly Support the inverse philosophy of the base-declares approach: the subtype declares itself as a child of the base through [KnownSubTypeOf(typeof(Base), "value")], and the host registers the plugin assembly at runtime through RegisterSubtypeAssembly. This covers the real plugin scenario the attribute cannot: assemblies loaded at runtime whose names are unknown at compile time. The scan mirrors the runtime cross-assembly approach: register the assembly, scan its types for self-declared subtypes, and map those that carry a discriminator value; types without a value resolve by name in the registered assembly. ReflectionTypeLoadException is handled so an assembly with unloadable optional types is not fatal. Both philosophies coexist: the base can still name other assemblies via [KnownSubTypeOtherAssembly].
The generator already exposed RegisterDynamicSubtype, but the runtime converter did not: README claimed parity that did not exist. Add the same runtime hook to JsonSubtypes<T>, mapping a discriminator to a subtype after the converter is built, with last-registration-wins like the generator. This covers the simple plugin need (register a subtype at runtime) without editing the plugin or scanning an assembly, complementing RegisterSubtypeAssembly. NullableDictionary gains a Set method for the overwrite semantics.
…ropertyOf Mirror the KnownSubTypeOf work for property-presence discrimination. The subtype declares itself through [KnownSubTypeWithPropertyOf(typeof(Base), "Property")] and JsonSubtypesWithPropertyConverterBuilder.RegisterSubtypeAssembly scans the registered assembly for those declarations, mapping the property presence. Same ReflectionTypeLoadException handling as the value-based scan.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Runtime-loaded subtypes (plugins, assemblies loaded at runtime, config-driven types) could not self-declare in the STJ engine: the only cross-assembly mechanism was a process-wide assembly registry, and per-subtype runtime registration did not exist.
Fix
Tests
Honest note(s)