Skip to content

Improve surrogate (serialization) performance#589

Closed
Daniel-Svensson wants to merge 1 commit into
OpenRIAServices:mainfrom
Daniel-Svensson:surrogate_perf
Closed

Improve surrogate (serialization) performance#589
Daniel-Svensson wants to merge 1 commit into
OpenRIAServices:mainfrom
Daniel-Svensson:surrogate_perf

Conversation

@Daniel-Svensson

@Daniel-Svensson Daniel-Svensson commented Jun 11, 2026

Copy link
Copy Markdown
Member

This pull request focuses on improving the performance of surrogate type creation in the WCF server serialization logic, which should positively impact serialization performance. The main changes involve optimizing how surrogate types and their factories are stored and accessed, as well as simplifying some of the logic for handling these types.

Documentation updates:

  • Updated the changelog to document the performance improvements for surrogate type creation and serialization.

Summary by CodeRabbit

  • Performance Improvements

    • Significantly enhanced server-side serialization and deserialization performance through improved surrogate type caching mechanisms and streamlined lookup strategies
    • Optimized surrogate type creation and resolution processes, reducing lookup overhead and delivering faster data serialization operations with improved system responsiveness
  • Release

    • Version 5.9.0 now available

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

DomainServiceSerializationSurrogate now uses a thread-safe ConcurrentDictionary to cache surrogate type and factory lookups, improving serialization performance by reducing lookup overhead. Supporting deserialization methods and a new public accessor API are added. Changelog documents the 5.9.0 performance improvement.

Changes

Surrogate caching and serialization performance

Layer / File(s) Summary
Thread-safe cache infrastructure
src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs
System.Collections.Concurrent added to imports. Constructor now unions EntityTypes and ComplexTypes, generates surrogate types and factory delegates via DynamicMethodUtility, and populates ConcurrentDictionary mapping exposed CLR types to surrogate type and factory tuples using TryAdd.
Serialization path with GetOrAdd caching
src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs
GetObjectToSerialize returns original object when runtime type matches targetType, otherwise uses ConcurrentDictionary.GetOrAdd keyed by runtime type to either fetch or compute cached surrogate info. Computation resolves proxy/runtime types via GetSerializationType and invokes the cached factory or returns original object.
Deserialization fast-path and conditional cloning
src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs
GetDeserializedObject clones ICloneable objects only when runtime type is in cached surrogate-type set; otherwise returns original. New internal GetDeserializedObjectFast always clones without membership checking.
Public surrogate factory accessor
src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs
Public method TryGetSurrogateFactory(Type type) added to return the cached surrogate factory delegate or null.
Release notes for version 5.9.0
Changelog.md
New 5.9.0 release header and Server subsection bullet document improved performance for creating surrogate types via reduced lookup counts.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 The cache now flows with thread-safe grace,
Surrogates stored in their rightful place!
Lookups reduced, performance takes flight,
ConcurrentDictionary shines so bright. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve surrogate (serialization) performance' directly and specifically summarizes the main change in the changeset, which involves optimizing surrogate type creation and serialization performance through caching improvements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Changelog.md`:
- Line 36: Update the Changelog entry sentence to correct grammar (change
"affect serialization performance" to "affecting serialization performance" or
"impact serialization performance") and add a new bullet noting the public API
addition: mention the new method TryGetSurrogateFactory(Type type) that exposes
cached surrogate factory retrieval so consumers are aware of the API change and
its purpose.

In
`@src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs`:
- Line 89: Fix the typo in the inline comment inside
DomainServiceSerializationSurrogate: change "heirarchy" to the correct spelling
"hierarchy" in the comment that reads "If the type is not found, check the
inheritance heirarchy for a known type." so the comment becomes "If the type is
not found, check the inheritance hierarchy for a known type."
- Around line 104-111: Add an XML documentation comment to the public method
TryGetSurrogateFactory describing its purpose, the type parameter, and the
return value; specifically include a <summary> that explains it attempts to
retrieve a surrogate factory for a given exposed type from the internal cache, a
<param name="type"> describing the expected Type input, a <returns> that states
it returns the surrogate factory delegate when found or null when no mapping
exists, and a brief <remarks> noting that the returned Func<object,object>
produces surrogate instances and callers should handle a null result. Target the
TryGetSurrogateFactory(Type type) method in DomainServiceSerializationSurrogate.
- Around line 72-76: Add an XML documentation comment above the internal method
GetDeserializedObjectFast in DomainServiceSerializationSurrogate: include a
<summary> that describes the fast path behavior for deserializing
surrogate-typed objects, a <param name="obj"> describing the input object
expected to implement ICloneable, and a <returns> describing that the
cloned/deserialized object is returned; place the comment immediately above the
method declaration to comply with the internal API documentation guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2302b97e-f8c5-45aa-a65b-29e9e21e1099

📥 Commits

Reviewing files that changed from the base of the PR and between 585cb6c and 0e0e18e.

📒 Files selected for processing (2)
  • Changelog.md
  • src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs

Comment thread Changelog.md

## Server

* Improve performance for creating surrogate types, by reducing number of lookups (affect serialization performance)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Fix grammar and consider documenting the new public API.

Minor grammar issue: "affect serialization performance" should use the participle "affecting" or the verb "impacts" for better readability.

Additionally, the related code changes introduce a new public method TryGetSurrogateFactory(Type type) that exposes cached surrogate factory retrieval. Consider adding a bullet point documenting this new API, as public API additions are typically notable changelog entries.

📝 Suggested revision
-* Improve performance for creating surrogate types, by reducing number of lookups (affect serialization performance)
+* Improve performance for creating surrogate types, by reducing number of lookups (impacting serialization performance)
+* Add public API `TryGetSurrogateFactory(Type type)` to retrieve cached surrogate factory delegates
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Changelog.md` at line 36, Update the Changelog entry sentence to correct
grammar (change "affect serialization performance" to "affecting serialization
performance" or "impact serialization performance") and add a new bullet noting
the public API addition: mention the new method TryGetSurrogateFactory(Type
type) that exposes cached surrogate factory retrieval so consumers are aware of
the API change and its purpose.

Comment on lines +72 to +76
// Fast path to get deserialized object when we know the type is a surrogate, to avoid the dictionary lookup in GetDeserializedObject.
internal object GetDeserializedObjectFast(object obj)
{
return ((ICloneable)obj).Clone();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Add XML documentation comment for new internal API.

Per coding guidelines, new internal APIs should have XML documentation comments. The inline comment explains the intent well, but converting it to XML format would be more consistent.

📝 Suggested XML documentation
-        // Fast path to get deserialized object when we know the type is a surrogate, to avoid the dictionary lookup in GetDeserializedObject.
+        /// <summary>
+        /// Fast path to get deserialized object when the type is known to be a surrogate,
+        /// avoiding the dictionary lookup in <see cref="GetDeserializedObject"/>.
+        /// </summary>
+        /// <param name="obj">The surrogate object to clone.</param>
+        /// <returns>The cloned original entity or complex type instance.</returns>
         internal object GetDeserializedObjectFast(object obj)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Fast path to get deserialized object when we know the type is a surrogate, to avoid the dictionary lookup in GetDeserializedObject.
internal object GetDeserializedObjectFast(object obj)
{
return ((ICloneable)obj).Clone();
}
/// <summary>
/// Fast path to get deserialized object when the type is known to be a surrogate,
/// avoiding the dictionary lookup in <see cref="GetDeserializedObject"/>.
/// </summary>
/// <param name="obj">The surrogate object to clone.</param>
/// <returns>The cloned original entity or complex type instance.</returns>
internal object GetDeserializedObjectFast(object obj)
{
return ((ICloneable)obj).Clone();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs`
around lines 72 - 76, Add an XML documentation comment above the internal method
GetDeserializedObjectFast in DomainServiceSerializationSurrogate: include a
<summary> that describes the fast path behavior for deserializing
surrogate-typed objects, a <param name="obj"> describing the input object
expected to implement ICloneable, and a <returns> describing that the
cloned/deserialized object is returned; place the comment immediately above the
method declaration to comply with the internal API documentation guideline.

Source: Coding guidelines

var (_, surrogateFactory) = this._exposedTypeToSurrogateMap.GetOrAdd(type, static (key, This) =>
{
if (this.exposedTypeToSurrogateMap.TryGetValue(exposedType, out var surrogateInfo))
// If the type is not found, check the inheritance heirarchy for a known type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix typo: "heirarchy" → "hierarchy".

-                // If the type is not found, check the inheritance heirarchy for a known type.
+                // If the type is not found, check the inheritance hierarchy for a known type.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If the type is not found, check the inheritance heirarchy for a known type.
// If the type is not found, check the inheritance hierarchy for a known type.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs`
at line 89, Fix the typo in the inline comment inside
DomainServiceSerializationSurrogate: change "heirarchy" to the correct spelling
"hierarchy" in the comment that reads "If the type is not found, check the
inheritance heirarchy for a known type." so the comment becomes "If the type is
not found, check the inheritance hierarchy for a known type."

Comment on lines +104 to 111
public Func<object, object> TryGetSurrogateFactory(Type type)
{
if (this._exposedTypeToSurrogateMap.TryGetValue(type, out var surrogateInfo))
{
return surrogateInfo.surrogateFactory;
}
return null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add XML documentation comment for new public API.

Per coding guidelines, new public APIs should have XML documentation comments. This method exposes internal caching to consumers, so documenting its purpose, parameters, and return value is important.

📝 Suggested XML documentation
+        /// <summary>
+        /// Gets the surrogate factory delegate for the specified type, if available.
+        /// </summary>
+        /// <param name="type">The exposed CLR type to look up.</param>
+        /// <returns>
+        /// A factory delegate that creates a surrogate instance from an entity or complex type instance,
+        /// or <see langword="null"/> if no surrogate is registered for the specified type.
+        /// </returns>
         public Func<object, object> TryGetSurrogateFactory(Type type)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/OpenRiaServices.Hosting.Wcf/Framework/Wcf/DomainServiceSerializationSurrogate.cs`
around lines 104 - 111, Add an XML documentation comment to the public method
TryGetSurrogateFactory describing its purpose, the type parameter, and the
return value; specifically include a <summary> that explains it attempts to
retrieve a surrogate factory for a given exposed type from the internal cache, a
<param name="type"> describing the expected Type input, a <returns> that states
it returns the surrogate factory delegate when found or null when no mapping
exists, and a brief <remarks> noting that the returned Func<object,object>
produces surrogate instances and callers should handle a null result. Target the
TryGetSurrogateFactory(Type type) method in DomainServiceSerializationSurrogate.

Source: Coding guidelines

@Daniel-Svensson

Copy link
Copy Markdown
Member Author

no measurable perf change for binary serialization.
Wait until messagepack for improvements

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant