diff --git a/docs/core/compatibility/11.md b/docs/core/compatibility/11.md index 7af24d31fc0ef..ac2c77d9bbac3 100644 --- a/docs/core/compatibility/11.md +++ b/docs/core/compatibility/11.md @@ -69,6 +69,7 @@ See [Breaking changes in EF Core 11](/ef/core/what-is-new/ef-core-11.0/breaking- | [FileConfigurationProvider doesn't raise reload token after ignored load failure](extensions/11/fileconfigurationprovider-reload-token-load-failure.md) | Behavioral change | | [FileConfigurationSource.OnLoadException callback is called for IO errors](extensions/11/fileconfigurationsource-onloadexception-io-errors.md) | Behavioral change | | [IHost.RunAsync and IHost.StopAsync throw when a BackgroundService fails](extensions/11/ihost-runasync-stopasync-throw-backgroundservice-failure.md) | Behavioral change | +| [PhysicalFilesWatcher validates root and FileSystemWatcher paths](extensions/11/physicalfileswatcher-validates-root-path.md) | Behavioral change | | [Some Microsoft.Extensions packages included in shared framework](extensions/11/extensions-in-shared-framework.md) | Behavioral change | ## Globalization diff --git a/docs/core/compatibility/extensions/11/physicalfileswatcher-validates-root-path.md b/docs/core/compatibility/extensions/11/physicalfileswatcher-validates-root-path.md new file mode 100644 index 0000000000000..22f71f637b6a7 --- /dev/null +++ b/docs/core/compatibility/extensions/11/physicalfileswatcher-validates-root-path.md @@ -0,0 +1,92 @@ +--- +title: "Breaking change: PhysicalFilesWatcher validates root and FileSystemWatcher paths" +description: "Learn about the breaking change in .NET 11 where PhysicalFilesWatcher validates the root argument and the relationship between root and FileSystemWatcher.Path." +ms.date: 08/28/2026 +ai-usage: ai-assisted +--- + +# PhysicalFilesWatcher validates root and FileSystemWatcher paths + + 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. + +## Version introduced + +.NET 11 Preview 4 + +## Previous behavior + +Previously, the `PhysicalFilesWatcher` constructors stored the supplied `root` without validation or normalization. A `null` or otherwise invalid root could 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 couldn't report changes relevant to the configured root, but the mismatch didn't cause the constructor to throw. + +For example, the following construction succeeded: + +```csharp +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 + +Starting in .NET 11, the `PhysicalFilesWatcher` constructors normalize `root` by calling `Path.GetFullPath()` and reject invalid inputs at construction time: + +- If `root` is `null`, the constructor throws . +- If `root` can't 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 . + +`FileSystemWatcher.Path` is valid when it's 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 doesn't yet exist remains valid. File watching is deferred until the root is created. + +## Type of breaking change + +This change is a [behavioral change](../../categories.md#behavioral-change). + +## Reason for change + +`PhysicalFilesWatcher` now supports roots that don't exist when the watcher is constructed. This requires normalization of the root and coordination of a supplied `FileSystemWatcher` with that root. + +A `FileSystemWatcher` that monitors an unrelated directory can't 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 didn't work. Validation of the root also causes invalid paths to fail immediately instead of during a later watcher operation. For more information, see [dotnet/runtime#126411](https://github.com/dotnet/runtime/pull/126411). + +## Recommended action + +Pass a non-null, valid path as `root`. + +When you supply a `FileSystemWatcher` with a nonempty `Path`, configure its path to be equal to, an ancestor of, or a descendant of `root`. For example: + +```csharp +string root = Path.GetFullPath(configuredRoot); + +using var fileSystemWatcher = new FileSystemWatcher(root); +using var watcher = new PhysicalFilesWatcher( + root, + fileSystemWatcher, + pollForChanges: false); +``` + +If the root directory doesn't exist yet, an empty `FileSystemWatcher.Path` is valid: + +```csharp +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. + +## Affected APIs + +- [PhysicalFilesWatcher(String, FileSystemWatcher, Boolean) constructor](xref:Microsoft.Extensions.FileProviders.Physical.PhysicalFilesWatcher.%23ctor(System.String,System.IO.FileSystemWatcher,System.Boolean)) +- [PhysicalFilesWatcher(String, FileSystemWatcher, Boolean, Physical.ExclusionFilters) constructor](xref:Microsoft.Extensions.FileProviders.Physical.PhysicalFilesWatcher.%23ctor(System.String,System.IO.FileSystemWatcher,System.Boolean,Microsoft.Extensions.FileProviders.Physical.ExclusionFilters)) diff --git a/docs/core/compatibility/toc.yml b/docs/core/compatibility/toc.yml index 5ce6d19f922f4..f7ed617a0b231 100644 --- a/docs/core/compatibility/toc.yml +++ b/docs/core/compatibility/toc.yml @@ -68,6 +68,8 @@ items: href: extensions/11/fileconfigurationsource-onloadexception-io-errors.md - name: IHost.RunAsync and IHost.StopAsync throw when a BackgroundService fails href: extensions/11/ihost-runasync-stopasync-throw-backgroundservice-failure.md + - name: PhysicalFilesWatcher validates root and FileSystemWatcher paths + href: extensions/11/physicalfileswatcher-validates-root-path.md - name: Some Microsoft.Extensions packages included in shared framework href: extensions/11/extensions-in-shared-framework.md - name: Globalization