feat(codegen): register static grain-reference factories - #11386
ReubenBond wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Nested generic interfaces can produce invalid generated code, and duplicate manifest registrations can unexpectedly discard static factories.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Adds statically generated grain-reference factories to avoid reflective construction, particularly under NativeAOT.
Changes:
- Generates direct typed factory registrations, including closed generic interfaces.
- Resolves factories by declared interface while retaining legacy proxy compatibility.
- Adds runtime, code-generation, NativeAOT, API, and documentation coverage.
| File | Description |
|---|---|
test/Orleans.NativeAotSmoke/GrainReferences.cs |
Tests closed generic factories under NativeAOT. |
test/Orleans.Core.Tests/GrainReferences/ReferenceConstructionFixture.cs |
Extends the reference-construction fixture. |
test/Orleans.Core.Tests/GrainReferences/GrainReferenceActivatorTests.cs |
Covers factory resolution and compatibility. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestGrainWithMultipleInterfaces.verified.cs |
Updates generated factory snapshot. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestGrainWithDifferentKeyTypes.verified.cs |
Updates key-type factory snapshots. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestGrainMethodAnnotatedWithResponseTimeout.verified.cs |
Updates generated factory snapshot. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestGrainMethodAnnotatedWithInvokableBaseType.verified.cs |
Updates generated factory snapshot. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestGrainComplexGrain.verified.cs |
Updates generated factory snapshot. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestClassWithGenerateMethodSerializersAnnotation.verified.cs |
Updates generated factory snapshot. |
test/Orleans.CodeGenerator.Tests/snapshots/OrleansSourceGeneratorTests.TestBasicGrain.verified.cs |
Updates generated factory snapshot. |
test/Orleans.CodeGenerator.Tests/GrainReferenceFactoryCodegenTests.cs |
Tests factory code generation and diagnostics. |
src/Orleans.Serialization/Configuration/TypeManifestOptions.cs |
Adds typed manifest extension storage. |
src/Orleans.Core/GrainReferences/GrainReferenceActivator.cs |
Resolves and invokes registered factories. |
src/Orleans.Core.Abstractions/Configuration/GrainReferenceFactoryOptions.cs |
Defines factory registration options. |
src/Orleans.Core.Abstractions/CodeGeneration/GenerateGrainReferenceAttribute.cs |
Adds closed-interface generation attribute. |
src/Orleans.CodeGenerator/Model/ReferenceAssemblyModelExtractor.cs |
Extracts requested closed factories. |
src/Orleans.CodeGenerator/Model/ReferenceAssemblyModel.cs |
Models grain-reference factories. |
src/Orleans.CodeGenerator/Model/ProxyInterfaceModelExtractor.cs |
Detects compatible proxy bases. |
src/Orleans.CodeGenerator/Model/ProxyInterfaceModel.cs |
Records factory support metadata. |
src/Orleans.CodeGenerator/Model/MetadataAggregateModelBuilder.cs |
Preserves factory metadata during normalization. |
src/Orleans.CodeGenerator/MetadataGenerator.cs |
Emits direct factory registrations. |
src/Orleans.CodeGenerator/Diagnostics/InvalidGrainReferenceFactoryDiagnostic.cs |
Defines invalid-registration diagnostics. |
src/Orleans.CodeGenerator/Diagnostics/DiagnosticRuleId.cs |
Adds diagnostic identifier ORLEANS0117. |
src/Orleans.CodeGenerator/AnalyzerReleases.Unshipped.md |
Records the new analyzer rule. |
src/api/Orleans.Serialization/Orleans.Serialization.cs |
Updates generated serialization API surface. |
src/api/Orleans.Core.Abstractions/Orleans.Core.Abstractions.cs |
Updates generated core API surface. |
docs/site/src/content/docs/implementation/serialization.md |
Documents factory construction and compatibility. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Code coverage
Report-only conclusion: current-main baseline stale. The newest successful coverage run tested cdb7961, not current main d4e4620. Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities. The comparison remains report-only while normal line and branch variance is calibrated. Coverage details |


Warning
Incomplete approach: automatic reflection-free construction covers only non-generic grain references. Generic proxies have generated
Createmethods, but this PR does not automatically generate or register factories for closed generic grain interfaces. Without explicit closed registrations, generic reference construction still usesMakeGenericType, constructor lookup, andConstructorInvoker.The NativeAOT generic scenarios work because the tests manually register their closed proxy factories. They do not demonstrate automatic generic support. This falls short of replacing reflective construction for all grain references. This PR is draft, is not ready to merge, and will likely not be merged.
Follow-up to #11371.
Generated grain references use statically registered, typed factory delegates.
RpcProviderresolves them by the explicitly declared grain interface, avoiding interface guessing, constructor reflection, emitted IL, and grain-key boxing on the registered construction path. The existing activator continues to share runtime state, interface versions, invocation options, and serialization services while each reference retains its own key.Proxy bases declare their construction contract with
[GenerateProxyFactory(typeof(TDelegate))]. The generator reads the delegate's argument types, passing modes, and result type, emits a staticCreatemethod with a direct constructor call, and registers the method group inInterfaceProxyFactoryOptions<TDelegate>.GrainReferencedeclares the namedGrainReferenceFactorydelegate, which accepts shared runtime state and a grain key and returns a grain reference. The base annotation, registrations, cached activators, and legacy compatibility delegates all use that same named type. Factory generation and metadata registration remain independent of grain-specific types.Factory declarations are inherited, and a nearer proxy base can override the declaration. Closed delegates and supported argument passing modes are validated at build time; factory results use identity or implicit reference conversions to preserve the constructed proxy instance. C# checks constructor calls for both non-generic and open generic proxies. Unannotated RPC bases retain their existing type-only registration behavior. A public read-only registration view lets other proxy consumers invoke their own typed factories. Coverage includes non-grain factories with multiple arguments, generic proxy families, inheritance/overrides, custom delegates with
ref,in, andoutarguments, and rejection of user-defined result conversions.Factories for non-generic interfaces are registered automatically. Closed-generic NativeAOT construction uses explicitly supplied concrete factory registrations; open mappings support runtime-selected arguments on JIT through reflective generic closure. Older generated binaries and custom proxies registered by type retain the constructor-invoker compatibility path. Legacy custom overrides and replacement typed factories preserve their precedence. The earlier grain-specific root attribute, discovery models, and capability checks are removed.
The existing production grain-reference NativeAOT scenario exercises generated non-generic references, manually registered value/reference-type generic proxies, and a non-grain proxy whose factory comes solely from its base declaration. Generated proxy names remain implementation details. Public API surfaces are regenerated and architecture guidance describes factory contracts, registration, and compatibility boundaries.
Microsoft Reviewers: Open in CodeFlow