Skip to content

[Breaking change]: PhysicalFilesWatcher validates root and FileSystemWatcher paths #55756

Description

@svick

Description

The PhysicalFilesWatcher constructors now validate the root argument and the relationship between root and FileSystemWatcher.Path. Invalid combinations that were previously accepted at construction time now throw an exception.

This validation was added as part of the work to support watcher roots that do not yet exist. The new support for nonexistent roots is not itself a breaking change.

For more information, see dotnet/runtime#126411.

Version

.NET 11 Preview 4

Previous behavior

The PhysicalFilesWatcher constructors stored the supplied root without validating or normalizing it. A null or otherwise invalid root could therefore be accepted at construction time and fail during a later watcher operation.

The constructors also accepted a FileSystemWatcher whose nonempty Path was unrelated to root. Such a watcher generally could not report changes relevant to the configured root, but the mismatch did not cause the constructor to throw.

For example, the following construction succeeded:

string root = Path.Combine(Path.GetTempPath(), "root");
string unrelatedPath = Path.Combine(Path.GetTempPath(), "unrelated");
Directory.CreateDirectory(root);
Directory.CreateDirectory(unrelatedPath);

using var fileSystemWatcher = new FileSystemWatcher(unrelatedPath);
using var watcher = new PhysicalFilesWatcher(
    root,
    fileSystemWatcher,
    pollForChanges: false);

New behavior

The PhysicalFilesWatcher constructors normalize root by calling Path.GetFullPath() and reject invalid inputs at construction time:

  • If root is null, the constructor throws ArgumentNullException.
  • If root cannot be converted to a full path, the constructor propagates the applicable exception from Path.GetFullPath().
  • If FileSystemWatcher.Path is nonempty and is unrelated to root, the constructor throws ArgumentException.

FileSystemWatcher.Path is valid when it is empty, equal to root, an ancestor of root, or a descendant of root. In the previous example, construction now throws ArgumentException because unrelatedPath is neither an ancestor nor a descendant of root.

A root directory that does not yet exist remains valid. File watching is deferred until the root is created.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

PhysicalFilesWatcher now supports roots that do not exist when the watcher is constructed. This requires normalizing the root and coordinating a supplied FileSystemWatcher with that root.

A FileSystemWatcher that monitors an unrelated directory cannot reliably produce notifications for the configured root. Rejecting this invalid combination at construction time prevents a watcher from being created in a configuration that generally did not work. Validating the root also causes invalid paths to fail immediately instead of during a later watcher operation.

Recommended action

Pass a non-null, valid path as root.

When supplying a FileSystemWatcher with a nonempty Path, configure its path to be equal to, an ancestor of, or a descendant of root. For example:

string root = Path.GetFullPath(configuredRoot);

using var fileSystemWatcher = new FileSystemWatcher(root);
using var watcher = new PhysicalFilesWatcher(
    root,
    fileSystemWatcher,
    pollForChanges: false);

If the root directory does not exist yet, an empty FileSystemWatcher.Path is valid:

string root = Path.GetFullPath(configuredRoot);

using var fileSystemWatcher = new FileSystemWatcher();
using var watcher = new PhysicalFilesWatcher(
    root,
    fileSystemWatcher,
    pollForChanges: false);

The watcher begins monitoring after the root directory is created.

Feature area

Extensions

Affected APIs

  • Microsoft.Extensions.FileProviders.Physical.PhysicalFilesWatcher.PhysicalFilesWatcher(string, System.IO.FileSystemWatcher?, bool)
  • Microsoft.Extensions.FileProviders.Physical.PhysicalFilesWatcher.PhysicalFilesWatcher(string, System.IO.FileSystemWatcher?, bool, Microsoft.Extensions.FileProviders.Physical.ExclusionFilters)

Note

This issue was drafted with GitHub Copilot.

Metadata

Metadata

Assignees

Labels

🗺️ reQUESTTriggers an issue to be imported into Quest.breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

Status
🏗 In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions