Skip to content

Fix | Scope configurable retry logic assembly resolution to opt-in callers - #4547

Draft
priyankatiwari08 wants to merge 1 commit into
mainfrom
dev/prtiwar/fix-configurable-retry-assembly-resolution
Draft

Fix | Scope configurable retry logic assembly resolution to opt-in callers#4547
priyankatiwari08 wants to merge 1 commit into
mainfrom
dev/prtiwar/fix-configurable-retry-assembly-resolution

Conversation

@priyankatiwari08

Copy link
Copy Markdown
Contributor

Summary

SqlConfigurableRetryLogicLoader subscribed a handler to AssemblyLoadContext.Default.Resolving in its constructor and never removed it.

SqlConfigurableRetryLogicManager builds that loader lazily on the default RetryLogicProvider path, so simply reading SqlCommand.RetryLogicProvider or SqlConnection.RetryLogicProvider — which any app using retry logic does — installed a permanent, process-wide assembly resolution hook.

Two consequences:

  1. It applied to everything. Once installed, the handler participated in resolving every assembly the host application failed to find, even though the application had never configured a custom retry logic type. Apps saw failures (and in Stack overflow caused by SqlConfigurableRetryLogicLoader.Defaul_resolving #2214 a stack overflow) surfacing from inside SqlClient for assemblies that have nothing to do with SqlClient.
  2. It probed the wrong directory. The handler resolved against Environment.CurrentDirectory. The working directory is ambient process state that any code in the process can change and that bears no relationship to where the application's binaries live, so assemblies could be resolved from an unintended location.

Only .NET is affected — the .NET Framework code path does not use AssemblyLoadContext.

Changes

# Change
1 Probe AppContext.BaseDirectory instead of Environment.CurrentDirectory.
2 Subscribe the resolving handler only for the duration of the Type.GetType call in LoadType, removing it in a finally.
3 Skip type resolution entirely when no retryLogicType is configured.

On (3): in AppConfigManager, retryLogicType is optional while retryMethod is IsRequired = true. So a config that selected a built-in retry method still went through the custom-type resolution path and installed the handler. It now short-circuits straight to SqlConfigurableRetryFactory.

Net effect: the handler is never installed unless the application explicitly configured a custom retry logic type, and it is removed again as soon as that type is resolved.

Tests

  • New SqlConfigurableRetryLogicLoaderTest (UnitTests — needs InternalsVisibleTo, which FunctionalTests does not have). Asserts no delegate declared by SqlConfigurableRetryLogicLoader remains attached to AssemblyLoadContext.Default.Resolving for three cases: no configuration, config without retryLogicType, and config with an unresolvable retryLogicType.
  • New RetryLogicProviderDoesNotEnableCurrentDirectoryAssemblyProbing (FunctionalTests). Writes a non-assembly file named <name>.dll into a temp directory, makes it the working directory, and asserts Assembly.Load reports FileNotFoundException rather than BadImageFormatException.

Both were confirmed to fail against the pre-fix code and pass after.

Validated on net462, net8.0, net9.0, and net10.0.

Checklist

Suggested release note entry

Fixed SqlConfigurableRetryLogicLoader installing a permanent, process-wide AssemblyLoadContext.Default.Resolving handler that probed the current working directory. The handler is now scoped to custom retry logic type resolution only and probes the application base directory. (#2214)

Related

Refs #2214, #2134

SqlConfigurableRetryLogicLoader subscribed a handler to
AssemblyLoadContext.Default.Resolving in its constructor and never removed
it. Because SqlConfigurableRetryLogicManager builds that loader on the
default RetryLogicProvider path, simply reading
SqlCommand.RetryLogicProvider or SqlConnection.RetryLogicProvider installed
a permanent, process-wide assembly resolution hook.

The hook then participated in resolving every assembly the host application
failed to find, even though the application had not configured any custom
retry logic type. It also probed Environment.CurrentDirectory, which is
ambient process state unrelated to where the application's binaries live,
so assemblies could be resolved from an unintended location.

Applications observed this as load failures, and in #2214 as a stack
overflow, originating inside SqlClient for assemblies unrelated to
SqlClient.

Changes:

- Probe AppContext.BaseDirectory instead of Environment.CurrentDirectory.
- Subscribe the resolving handler only for the duration of the Type.GetType
  call in LoadType, and remove it in a finally block.
- Skip type resolution entirely when no retryLogicType is configured.
  retryLogicType is optional while retryMethod is required, so
  configurations selecting a built-in retry method previously still ran the
  custom type resolution path.

Together these mean the handler is never installed unless the application
explicitly configured a custom retry logic type, and is gone again as soon
as that type has been resolved.

Only .NET is affected; the .NET Framework code path does not use
AssemblyLoadContext.

Refs #2214, #2134

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e8018b20-0e43-4de9-93dd-c8080a81a801
Copilot AI lite review requested due to automatic review settings August 18, 2026 09:35
@priyankatiwari08
priyankatiwari08 requested a review from a team as a code owner August 18, 2026 09:35
@github-project-automation github-project-automation Bot moved this to To triage in SqlClient Board Aug 18, 2026

Copilot AI 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.

Pull request overview

This PR fixes SqlConfigurableRetryLogicLoader’s .NET AssemblyLoadContext.Default.Resolving usage so it only applies to explicit opt-in callers (custom retry-logic type resolution) and no longer probes the process working directory.

Changes:

  • Switch configurable retry-logic assembly probing from Environment.CurrentDirectory to AppContext.BaseDirectory.
  • Scope AssemblyLoadContext.Default.Resolving subscription to the duration of the Type.GetType(...) call (subscribe + finally unsubscribe).
  • Skip custom type resolution entirely when retryLogicType is not configured (use built-in factory directly).
  • Add unit + functional regression tests validating the handler is not left attached and that current-directory probing is not enabled.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Reliability/SqlConfigurableRetryLogicLoader.cs Scopes the resolving handler to opt-in type resolution and switches probing to AppContext.BaseDirectory.
src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlConfigurableRetryLogicLoaderTest.cs New unit tests validating no SqlConfigurableRetryLogicLoader resolving delegates remain attached.
src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlConfigurableRetryLogicTest.cs Adds functional regression test ensuring retry-logic initialization does not enable current-directory assembly probing.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 5 to 9
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
Comment on lines +102 to +106
string probeDirectory = Path.Combine(Path.GetTempPath(), "mds-crl-plant-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(probeDirectory);
string originalCurrentDirectory = Environment.CurrentDirectory;

try
@priyankatiwari08
priyankatiwari08 marked this pull request as draft August 19, 2026 09:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To triage

Development

Successfully merging this pull request may close these issues.

2 participants