Home > Software engineering >  FileSystemWatcher is not working with dotnet Background Workers
FileSystemWatcher is not working with dotnet Background Workers

Time:07-01

I am trying to create a windows service to watch the file changes in a specific directory. I am using dotnet core 6 and a BackgroundService.

I created a separate class named FileMonitor in which I simply paste the Microsoft FileSystemWatcher example

public void MyWatcher(string folder)
        {
            using var watcher = new FileSystemWatcher(folder);

            watcher.NotifyFilter = NotifyFilters.Attributes
                                 | NotifyFilters.CreationTime
                                 | NotifyFilters.DirectoryName
                                 | NotifyFilters.FileName;

            watcher.Changed  = OnChanged;
            watcher.Created  = OnCreated;

            watcher.Filter = "*.txt";
            watcher.EnableRaisingEvents = true;
        }

        private void OnChanged(object sender, FileSystemEventArgs e)
        {
            if (e.ChangeType != WatcherChangeTypes.Changed)
            {
                return;
            }
            Console.WriteLine($"Changed: {e.FullPath}");
        }

        private void OnCreated(object sender, FileSystemEventArgs e)
        {
            string value = $"Created: {e.FullPath}";
            Console.WriteLine(value);
        }

And in my BackgroundService I am creating an instance of FileMonitor and calling the method MyWatcher as below.

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            FileMonitor fm = new FileMonitor();
            fm.MyWatcher($"C:\test");
            while (!stoppingToken.IsCancellationRequested)
            {
                
            }
        }

I am not getting any console logs about the file change

CodePudding user response:

using var watcher = new FileSystemWatcher(folder);

That using right there will dispose of the watcher at the end of that function, so as soon as it sets up the events. They will never get called.

You need to keep the objects alive and rooted for their entire lifetime. Looking at your calling pattern, you should store them in an instance field of type List<FileSystemWatcher>.

Also your ExecuteAsync isn't async, despite its name. You need to relinquish your thread at some point, by awaiting either Task.Delay(...) or Task.Yield() in your while loop.

CodePudding user response:

As mentioned, you have a problem that you are disposing the watcher before your task completes.

You also have another issue that your BackgroundTask is running an infinite loop and will chew through CPU.

For this and other reasons, I recommend you transform this fully into the async pattern.

public Task MyWatcher(string folder, CancellationToken stoppingToken)
{
    var watcher = new FileSystemWatcher(folder)
    {
        NotifyFilter = NotifyFilters.Attributes
                     | NotifyFilters.CreationTime
                     | NotifyFilters.DirectoryName
                     | NotifyFilters.FileName,
        Filter = "*.txt",
    };
    watcher.Changed  = OnChanged;
    watcher.Created  = OnCreated;
    watcher.EnableRaisingEvents = true;

    var tcs = new TaskCompletionSource();
    _ = stoppingToken.Register(() => { watcher.Dispose(); tcs.TrySetResult(); });
    return tcs.Task;
}

private void OnChanged(object sender, FileSystemEventArgs e)
{
    if (e.ChangeType != WatcherChangeTypes.Changed)
    {
        return;
    }
    Console.WriteLine($"Changed: {e.FullPath}");
}

private void OnCreated(object sender, FileSystemEventArgs e)
{
    string value = $"Created: {e.FullPath}";
    Console.WriteLine(value);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    FileMonitor fm = new FileMonitor();
    await fm.MyWatcher($"C:\test", stoppingToken);
}

dotnetfiddle

  • Related