# Wednesday, February 27, 2008
Update: a few people have questioned why I did not use the FileSystemWatcher class. If you do NOT need to check for the EXISTENCE (different than the CREATION) of a file in a particular directory and you do NOT need RegEx patterns, then FSW would be more efficient. If, however, like me, you need to look for the creation of a file in a particular directory via a more fail-safe approach (i.e.: to still pick up on files created even when the launching process errored or exited unexpectedly), you will need something like what I have coded, below.

What is a Directory Poller?

A directory poller is a process that monitors a file system directory for the existence of any files that meet a particular filename pattern and does something accordingly.

When the Directory Poller finds a file that matches, what does it do?

Being that this is a generic utility class, it does pretty much what you want it to do. Just pass in a reference to a method (or pass an inline anonymous method) that matches the delegate ProcessFile's signature. This method will execute once for every file in the directory whose filename matches your specified pattern.

Show me a practical example!

Let's say that you need to write an application that monitors a directory for text files with the naming pattern of "data1.txt", "data2.txt", etc. and imports the data from those files into a database. As such, you would've had to write a class whose method reads the file into the database and on completion removes it from the directory. Let's say its signature was a static method DBUtils.ImportText(string filePath). In order "wire up" this method to the DirectoryPoller class, you would write the code like this:
DirectoryPoller dp = new DirectoryPoller();
dp.PollingDirectory = @"c:\temp\poll\";
dp.AddFileProcess("data[0-9]+", DBUtils.ImportText(filePath));
dp.Poll();
The 2 arguments to the AddFileProcess method are 1) a string that represents a Regular Expression for specifying the filename pattern and 2) a delegate (ProcessFile) that accepts a reference to the method you want the DirectoryPoller to run. When the Poll() method is called, that referenced method (known as a delegate) you supplied is run for each file found that matches the Regular Expression pattern you also supplied. Typically, the Poll() method would be called in a process that runs periodically.

Note: you may add multiple file processes to the DirectoryPoller, i.e.: multiple calls to the AddFileProcess() method are allowed. The DirectoryPoller will only match a file once, even if multiple filename patterns apply.

You mentioned that an anonymous method could be used as well?

Yes, if you need to reference a fairly simple method to be run by the DirectoryPoller, you can use an anonymous method in an inline fashion. For instance, to simply display the name of the files that the DirectoryPoller finds, you would use the following code:
DirectoryPoller dp = new DirectoryPoller();
dp.PollingDirectory = @"c:\temp\poll\";
dp.AddFileProcess("data[0-9]+", delegate(string filePath)
    { Console.Out.WriteLine("found file: " + filePath); });
dp.Poll();
If you are using .NET 3.5. you can use a lambda expression in the place of the anonymous method, like this:
dp.AddFileProcess("data[0-9]+", filePath => { Console.Out.WriteLine("found file: " + filePath); });

Show me the DirectoryPoller code!

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

namespace Utility
{

    public delegate void ProcessFile(string filePath);

    public class DirectoryPoller
    {

        private string _pollingDirectory = String.Empty;
        private Dictionary _fileProcesses = new Dictionary();

        public string PollingDirectory
        {
            get
            {
                return _pollingDirectory;
            }
            set
            {
                _pollingDirectory = value;
            }
        }

        public void Poll()
        {

            if (_fileProcesses != null && _fileProcesses.Count > 0)
            {

                if (Directory.Exists(_pollingDirectory))
                {

                    foreach (string path in Directory.GetFiles(_pollingDirectory, "*.*",
                        SearchOption.TopDirectoryOnly))
                    {

                        foreach (string fileRegEx in _fileProcesses.Keys)
                        {

                            if (Regex.IsMatch(Path.GetFileName(path), fileRegEx)) //filename matches
                            {

                                _fileProcesses[fileRegEx].Invoke(path); //invoke delegate
                                break; //match only on one RegEx

                            }

                        }

                    }

                }
                else
                {
                    throw new ApplicationException(String.Format("Directory {0} not found.",
                        _pollingDirectory));
                }

            }

        }

        public void AddFileProcess(string fileRegEx, ProcessFile processMethod)
        {

            _fileProcesses.Add(fileRegEx, processMethod);

        }

    }
}
Saturday, March 01, 2008 11:12:20 AM (Central Standard Time, UTC-06:00)
Does this provide any advantage to .NET's FileSystemWatcher class?
Slarty
Sunday, March 02, 2008 11:23:34 PM (Central Standard Time, UTC-06:00)
None really that I can see beyond regular expression support. Needless to say polling is nowhere nearly as efficient as using FileSystemWatcher. Perhaps a hybrid that ran the regex on results from FileSystemWatcher?

Anyway, there's a nasty bug in Poll:

"break; //match only on one RegEx"

I have no idea why anyone would want a RegEx based file finder that would return only one result? If you want tighter control of the results, perhaps a yield return for results instead of delivering them to a lambda?
Monday, March 03, 2008 10:57:32 AM (Central Standard Time, UTC-06:00)
Slarty,

I wasn't just looking for the Creation of a file, I was looking for the EXISTENCE of a file.

This is more fail-safe for my needs. For instance, if the FSW wasn't running when the file was created, then the event wouldn't be triggered ever again. I was looking for something more fail safe.
Troy DeMonbreun
Monday, March 03, 2008 11:04:42 AM (Central Standard Time, UTC-06:00)
Sean,

I think you may be misunderstanding the code. It's not a bug, hence the comment. It breaks out of the RegEx collection foreach, not the GetFiles collection. The break is there so I don't run a delegate more than once per file, as one delegate could alter the file in a way the second delegate didn't predict. This is by design. If you need a different design, then it is easily removed.

BTW, that is a good idea to utilize FSW using the same/similar logic, _IF_ I didn't need to check for pre-existing files.

I'll look into the yield, as well; it sounds promising.

(However, I'm not sure I'll write an updated version of code, I doubt there's that much interest to begin with.)
Troy DeMonbreun
Monday, March 03, 2008 11:21:00 PM (Central Standard Time, UTC-06:00)
Whoops you are right, my bad.
All comments require the approval of the site owner before being displayed.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: b, blockquote@cite, em, i, strike, strong, sub, sup, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview