Note: This is part of a series, you can find the rest of the parts in the series index.
Pull started as a learning exercise and so I didn’t feel bad about using a new technology for a component which may not be the best choice. IMPF was one such are where I decided to use a new technology, however it hasn’t been smooth sailing – but I persisted as it was important for me to learn.
I was working on some enhancements and bug fixes recently and I ended having to put yet another hack into IMPF to handle the technology behind the scenes. In this case a Thread.Sleep to have a partial delay between when things are written and when they are read. This was a wake up bell for me, because it felt dirty and stupid. So what did I do? I decided the best course of action was to step back and think what is the best way to do this – and so I ripped all the MemoryMappedFile file out and the Win32 API stuff (about 200 lines of code) and replaced it with a WCF service/client implementation.
To do that I needed the contract, which is about as simple as it can get:
[ServiceContract] interface IDataService { [OperationContract] void SendMessage(string message); }
Next I needed the implementation, which is also very simple in a large part thanks to the bus.
class DataService : IDataService { public void SendMessage(string message) { IBus bus = Factory.Instance.Resolve<IBus>(); if (!string.IsNullOrWhiteSpace(message)) { if (message[0] == '!') { switch (message) { case "!playunplayed": { bus.Broadcast(DataAction.LaunchUnplayedEpisode); break; } case "!forcerefresh": { bus.Broadcast(DataAction.UpdateFeeds); break; } } } else { // its a feed bus.Broadcast(DataAction.ParseFeed, message); } } } }
You may note some command parameter items above, this is for new features I am working on.
Finally I needed a way to create the WCF service (the server) and a client to talk to it. I am using .NET 4 which means I get access to the great AddDefaultEndpoints method which makes this really simple. It figures out everything it needs for a default configuration from the URI that is passed. In my case I pass in a net.pipe URI so it sets up a named pipe for me.
public IPMF(string instance) { host = new ServiceHost(typeof(DataService), new Uri(InstanceURL(instance))); host.AddDefaultEndpoints(); host.Open(); }
Lastly sending a message to the named pipe is also very simple. You’ll note that I am using a ChannelFactory here and not an actual implementation. This is because I did not add a service reference, as the application has all the information it needs internally there is no need for additional code to be written.
public static void SendMessageToServer(string instance, string message) { IDataService dataService = ChannelFactory<IDataService>.CreateChannel(new NetNamedPipeBinding(), new EndpointAddress(InstanceURL(instance))); dataService.SendMessage(message); }
Final Thoughts
This is the correct way to do this, and it is what you should be using in real systems not only because it is so much less code but also because it works perfectly – no need to insane hacks.
My learning with IMPF was not only about the exceptionally powerful MemoryMappedFile in .NET 4 but also when it should and shouldn’t be used.