问题
I want my endpoint to send out an event whenever a detects a file in a specific folder is dropped. I was able to get it to work by using a class which implements IWantToRunWhenBusStartsAndStops, which in turn sets up a FileSystemWatcher to monitor the given folder. My questions is, is this the best way to go about this with nservicebus or am I missing something that may cause me trouble down the line?
Here is my code:
public class FileSystem : IWantToRunWhenBusStartsAndStops
{
private FileSystemWatcher watcher;
public void Start()
{
ConfigFileWatcher();
}
public void Stop()
{
}
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
private void ConfigFileWatcher()
{
watcher = new FileSystemWatcher();
watcher.Path = @"c:\";
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = "*.txt";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
// Begin watching.
watcher.EnableRaisingEvents = true;
}
// Define the event handlers.
private static void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed, created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
// fire off an event here...
}
}
回答1:
If you look at the NServiceBus source code, in the container initialization, you will see that IWantToRunWhenBusStartsAndStops is registered with the life cycle of one per call
ForAllTypes<IWantToRunWhenBusStartsAndStops>(TypesToScan, t => configurer.ConfigureComponent(t, DependencyLifecycle.InstancePerCall));
This means that the class will be disposed after Start() is called. Your implementation works because your events are subscribed to a static handler, which keeps subscriptions alive.
We use file watchers in production but we bake them as advanced satellites. Satellites are guaranteed to be initialized as singletons and will not be disposed. They have Start and Stop methods as well. They do have addresses and should be able to handle incoming messages but you can use some dummy address and do nothing in the handler unless you want to make your file system watcher satellite bidirectional (ie receive messages and put them as files on the disk).
In NServiceBus it is recommended to make constantly running, non-disposable processes as satellites. Many NServiceBus components are made as satellites.
You might get curious how to make a satellite yourself, but it is quite easy to do. You can check the interface signature here.
It will look something like this
using System;
using System.IO;
using NServiceBus;
using NServiceBus.Satellites;
public class FileSystem : ISatellite
{
private FileSystemWatcher _watcher;
public bool Handle(TransportMessage message)
{
return true;
}
public void Start()
{
_watcher = new FileSystemWatcher
{
Path = @"c:\",
NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName,
Filter = "*.txt"
};
_watcher.Changed += OnChanged;
_watcher.Created += OnChanged;
_watcher.Deleted += OnChanged;
_watcher.EnableRaisingEvents = true;
}
public void Stop()
{
_watcher.Dispose();
}
public Address InputAddress
{
get { return Address.Parse("FileSystemSatellite"); }
}
public bool Disabled
{
get { return false; }
}
// Define the event handlers.
private void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is changed, created, or deleted.
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
// fire off an event here...
}
}
There one thing you need to remember: each satellite gets its own queue. In this case it will always be empty.
回答2:
Based on the other answer and comments, I believe your FileSystemWatcher is being kept alive by its subscription to the OnChanged event.
This is actually a (fairly) common cause of memory leaks, though in your case it actually keeps your watcher in memory and allows it to continue operating. Remove the static keyword from your OnChanged() method, and I believe you will see the behavior that you would expect.
Of course, you want the FileSystemWatcher to keep functioning . . . one option might be to just make the watcher variable static itself. If you go that route, I would put it in a separate class, and just initialize it via the class that implements IWantToRunWhenBusStartsAndStops.
来源:https://stackoverflow.com/questions/29170342/nservicebus-with-file-system-watcher