Я создал этот тест, используя .NET 8 в Ubuntu 24.04:
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using Shouldly;
[TestFixture]
public class PhysicalFileProviderOnChangeTests
{
private string _tempDirectory;
private string _testFilePath;
[SetUp]
public void SetUp()
{
_tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(_tempDirectory);
_testFilePath = Path.Combine(_tempDirectory, "testfile.txt");
File.WriteAllText(_testFilePath, "Hello, World!");
var provider = new PhysicalFileProvider(_tempDirectory);
}
[Test]
public async Task TestOnChange_Handler()
{
var provider = new PhysicalFileProvider(_tempDirectory);
var changeToken = provider.Watch("testfile.txt");
var changeDetected = false;
ChangeToken.OnChange(
() => changeToken,
() => changeDetected = true
);
File.WriteAllText(_testFilePath, "New Content");
await Task.Delay(100);
changeDetected.ShouldBeTrue();
}
[TearDown]
public void TearDown()
{
if (Directory.Exists(_tempDirectory))
{
Directory.Delete(_tempDirectory, true);
}
}
}
При запуске этого теста с использованием dotnet test
я получаю следующее исключение:
The active test run was aborted. Reason: Test host process crashed : Stack overflow.
Repeat 10915 times:
--------------------------------
at System.Threading.CancellationTokenSource.Register(System.Delegate, System.Object, System.Threading.SynchronizationContext, Sys
tem.Threading.ExecutionContext)
at System.Threading.CancellationToken.Register(System.Delegate, System.Object, Boolean, Boolean)
at Microsoft.Extensions.Internal.ChangeCallbackRegistrar.UnsafeRegisterChangeCallback[[System.__Canon, System.Private.CoreLib, Ve
rsion=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Action`1<System.Object>, System.Object, System.Threading.Ca
ncellationToken, System.Action`1<System.__Canon>, System.__Canon)
at Microsoft.Extensions.Primitives.CancellationChangeToken.RegisterChangeCallback(System.Action`1<System.Object>, System.Object)
at Microsoft.Extensions.Primitives.ChangeToken+ChangeTokenRegistration`1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0
, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RegisterChangeTokenCallback(Microsoft.Extensions.Primitives.IChangeToken)
at Microsoft.Extensions.Primitives.ChangeToken+ChangeTokenRegistration`1[[System.__Canon, System.Private.CoreLib, Version=8.0.0.0
, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].OnChangeTokenFired()
at Microsoft.Extensions.Primitives.ChangeToken+ChangeTokenRegistration`1+<>c[[System.__Canon, System.Private.CoreLib, Version=8.0
.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].<RegisterChangeTokenCallback>b__7_0(System.Object)
at System.Threading.CancellationTokenSource.Invoke(System.Delegate, System.Object, System.Threading.CancellationTokenSource)
--------------------------------
at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean)
at Microsoft.Extensions.FileProviders.Physical.PhysicalFilesWatcher+<>c.<.cctor>b__43_0(System.Object)
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread, System.Threading.ExecutionContext, Sy
stem.Threading.ContextCallback, System.Object)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool+WorkerThread.WorkerThreadStart()
Запустив тот же тест в другом проекте, все работает.
Если закомментировать этот блок, тест завершится неудачно из-за утверждения «истина/ложь», но не произойдет сбой:
ChangeToken.OnChange(
() => changeToken,
() => changeDetected = true
);
В среде выполнения dotNet существует открытая проблема (с необъединённым потенциальным исправлением).
С потенциальным обходным решением, упомянутым там:
Известный обходной путь заключается в том, чтобы гарантировать, что производитель либо возвращает новый токен изменения, либо, по крайней мере, оценивает предыдущее состояние и сбрасывает себя (если это возможно).
Поэтому можно попытаться использовать этот обходной путь с чем-то вроде:
ChangeToken.OnChange(
() => new PhysicalFileProvider(_tempDirectory).Watch("testfile.txt"),
() => changeDetected = true
);
Спасибо, я тоже нашел проблему. Не знаю, как создать новый токен, но ваше предложение имеет смысл. Однако сейчас я повторно пользуюсь существующим провайдером и звоню
provider.Watch("testfile.txt");