Commit 11b6d7c
authored
.Net: fix: Fix AOT Compatibility - Remove Expression.Compile() from Brave/Tavily TextSearch (#13541)
# PR: Fix AOT Compatibility - Remove Expression.Compile() from
Brave/Tavily TextSearch
## Motivation and Context
This PR addresses **Critical Issue #1** from Copilot's code review
feedback on PR #13384 (feature-text-search-linq → main).
**Problem:**
The `ExtractValue()` method in both `BraveTextSearch` and
`TavilyTextSearch` uses
`Expression.Lambda(expression).Compile().DynamicInvoke()` as a fallback
case, which breaks NativeAOT compatibility. This directly contradicts
the ADR-0065 architectural decision that states:
> "Both interfaces are AOT-compatible with no `[RequiresDynamicCode]`
attributes"
**Impact:**
- Breaks NativeAOT compilation scenarios
- Violates Semantic Kernel's AOT compatibility requirements
- Contradicts documented architectural decisions
- Blocks deployment scenarios requiring AOT
**Root Cause:**
The issue was introduced in PR #13191 (Tavily/Brave connector
modernization) without AOT consideration.
## Description
Replaces the AOT-incompatible
`Expression.Lambda(expression).Compile().DynamicInvoke()` fallback with
a `NotSupportedException` that:
- Maintains AOT compatibility (no dynamic code generation)
- Provides clear error message explaining supported expression types
- Includes expression type and value for debugging
- Guides users to use only constant expressions and simple member access
## Changes Made
### Files Modified
- `dotnet/src/Plugins/Plugins.Web/Brave/BraveTextSearch.cs`
- `dotnet/src/Plugins/Plugins.Web/Tavily/TavilyTextSearch.cs`
### Before (AOT-incompatible):
```csharp
private static object? ExtractValue(Expression expression)
{
return expression switch
{
ConstantExpression constant => constant.Value,
MemberExpression member when member.Expression is ConstantExpression constantExpr =>
member.Member switch
{
System.Reflection.FieldInfo field => field.GetValue(constantExpr.Value),
System.Reflection.PropertyInfo property => property.GetValue(constantExpr.Value),
_ => null
},
_ => Expression.Lambda(expression).Compile().DynamicInvoke() // ❌ BREAKS AOT
};
}
```
### After (AOT-compatible):
```csharp
private static object? ExtractValue(Expression expression)
{
return expression switch
{
ConstantExpression constant => constant.Value,
MemberExpression member when member.Expression is ConstantExpression constantExpr =>
member.Member switch
{
System.Reflection.FieldInfo field => field.GetValue(constantExpr.Value),
System.Reflection.PropertyInfo property => property.GetValue(constantExpr.Value),
_ => null
},
_ => throw new NotSupportedException(
$"Unable to extract value from expression of type '{expression.GetType().Name}'. " +
$"Only constant expressions and simple member access are supported for AOT compatibility. " +
$"Expression: {expression}")
};
}
```
## Supported Expression Patterns
✅ **Works (AOT-compatible):**
- Constant values: `page.Language == "en"`
- Simple variables: `var lang = "en"; page.Language == lang`
- Member access: `config.Language` (where `config` is a captured
variable)
❌ **Not Supported (would require dynamic compilation):**
- Computed values: `page.Language == someVariable`
- Method calls: `page.Language == GetLanguage()`
- Complex expressions: `page.Language == (isEnglish ? "en" : "fr")`
Users encountering the exception should extract the value to a variable
before using it in the filter expression.
## Testing
- [x] Verified changes compile successfully
- [x] Confirmed AOT compatibility (no `Expression.Compile()` usage)
- [x] Exception message provides clear guidance
- [ ] Unit tests pass (pending CI/CD validation)
## Related Issues/PRs
- **Parent PR:** #13384 (feature-text-search-linq → main)
- **Copilot Review:** Issue #1 - AOT Compatibility (Critical)
- **Root Cause:** PR #13191 (Tavily/Brave modernization)
- **ADR Reference:**
docs/decisions/0065-linq-based-text-search-filtering.md (PR #13335)
## Contribution Checklist
- [ ] The code builds clean without any errors or warnings
- [x] Appropriate tests have been added (existing tests remain)
- [x] Changes are documented as needed
- [x] AOT compatibility verified
## Reviewer Notes
@markwallace-microsoft, @westey-m
This is the first of two critical fixes for PR #13384. The fix:
1. Eliminates all `Expression.Compile()` usage for AOT compatibility
2. Provides clear error messages for unsupported expression patterns
3. Aligns implementation with ADR-0065's AOT compatibility guarantee
The exception should rarely be hit in practice since most filters use
simple constants or variables (which are handled by the first two
cases). Users with complex expressions will get immediate, clear
feedback on how to fix their code.
**Next Steps:**
- Issue #2 (Breaking Change - Legacy Result Type) requires design
decision
- Issues #3-4 (Moderate bugs) can be addressed in follow-up PR
---------
Co-authored-by: Alexander Zarei <alzarei@users.noreply.github.com>1 parent c47eedd commit 11b6d7c
2 files changed
Lines changed: 50 additions & 16 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
496 | 496 | | |
497 | 497 | | |
498 | 498 | | |
499 | | - | |
500 | | - | |
501 | | - | |
502 | | - | |
503 | | - | |
504 | | - | |
505 | | - | |
506 | | - | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
507 | 524 | | |
508 | 525 | | |
509 | 526 | | |
| |||
Lines changed: 25 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
489 | 489 | | |
490 | 490 | | |
491 | 491 | | |
492 | | - | |
493 | | - | |
494 | | - | |
495 | | - | |
496 | | - | |
497 | | - | |
498 | | - | |
499 | | - | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
500 | 517 | | |
501 | 518 | | |
502 | 519 | | |
| |||
0 commit comments