You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AddObjectStatic picks a conversion path from each property's static type but dereferences the runtime value without a null check. A DTO with an unset optional field therefore crashes while the request is being built.
Fix AddObjectStatic null property handling to skip unset optional values
🐞 Bug fix🧪 Tests🕐 10-20 Minutes
AI Description
• Prevent AddObjectStatic from throwing when DTO properties are null.
• Skip null-valued properties to match reflection-based AddObject behavior.
• Add regression tests covering mixed null/non-null and all-null DTOs.
Diagram
graph TD
A["DTO instance"] --> B["RestRequest.AddObjectStatic"] --> C["PropertyCache.Populator.From"] --> D["getObject(model)"] --> E{"value is null?"}
E -->|"yes"| F["skip property"]
E -->|"no"| G["populate(model, parameters)"] --> H["Request.Parameters"]
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Add null checks inside each typed conversion path
➕ Keeps null-handling co-located with type-specific conversion logic
➕ Potentially allows type-specific null semantics (e.g., empty vs omitted)
➖ More code churn across multiple conversion paths
➖ Higher risk of missing a path and regressing behavior
2. Pre-filter properties with null values before building populators
➕ Avoids wrapping delegates per property
➕ Centralizes filtering logic in one place
➖ May require extra reflection/value reads up front
➖ Harder to keep parity with existing caching/compiled delegate strategy
Recommendation: The chosen wrapper delegate approach in Populator.From is a good fit: it’s minimal, consistently prevents dereferencing null runtime values regardless of static type, and explicitly aligns AddObjectStatic behavior with AddObject. Alternatives add broader churn or complicate caching without clear benefit.
Files changed (2) +70 / -1
Bug fix (1) +9 / -1
PropertyCache.Populator.csGuard AddObjectStatic populator against null runtime property values+9/-1
Guard AddObjectStatic populator against null runtime property values
• Wraps the generated per-property populate delegate with a runtime null check on the property getter. If the property value is null, the populator returns without adding parameters, matching reflection-based AddObject behavior.
• Introduces tests asserting AddObjectStatic skips null properties, retains non-null properties, yields no parameters when all are null, and matches AddObject behavior for equivalent inputs.
Populator.From now calls the property getter once for the null-check and again inside the cached
populate delegate, so non-null properties are evaluated twice. This can double side
effects/expensive getters and can still throw if the value changes to null between the two reads.
+ (model, parameters) => {+ if (getObject(model) is null) return;+ populate(model, parameters);+ }
Evidence
The new wrapper performs a null-check by calling getObject(model), but the populate delegate
produced by GetPopulate(getObject, property) reads the property again via getObject(entity)
during conversion/population, causing duplicate evaluations.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`Populator.From(PropertyInfo)` wraps the generated `populate` delegate with a null-check by calling `getObject(model)` and then invoking `populate(model, parameters)`. However, `populate` itself calls `getObject(entity)` again (via `GetPopulate(getObject, property)`), so each non-null property getter is invoked twice.
This changes behavior for stateful/non-idempotent getters and adds avoidable overhead.
### Issue Context
The fix for #2400 is correct (skip null values), but it should not require re-reading the property.
### Fix Focus Areas
- src/RestSharp/Request/PropertyCache.Populator.cs[54-86]
- src/RestSharp/Request/PropertyCache.Populator.cs[122-146]
### Suggested approach
Refactor so the getter is evaluated once per property population:
- Capture `var value = getObject(model);`
- If `value is null`, return.
- Use `value` for the conversion/population path (e.g., introduce a `GetPopulate` variant that accepts the already-fetched `object value`, or build the population logic in `From` based on `property.PropertyType` but operating on the captured `value`).
This preserves the null-skip behavior while avoiding duplicate getter evaluation.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Tip of the day
💡 Did you know, you can turn on the rule miner and Qodo learns your standards from review history
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
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.
Description
Closes #2400
Problem
AddObjectStaticpicks a conversion path from each property's static type but dereferences the runtime value without a null check. A DTO with an unset optional field therefore crashes while the request is being built.Purpose
This pull request is a:
Checklist