Note: This is part of a series, you can find the related posts here…
The last post used the new Application Extension Services (AES for short) to include security into the application. This time I’m going to take AES one step further, laying out yet another piece of basic infrastructure.
As a quick recap: An AES is a simple class, implementing a simple interface (IApplicationService) and optionally another one (IApplicationLifetimeAware). It is then registered by the developer via the app.xaml. SL3 instantiates the AES at runtime and calls the respective callback methods on said interfaces, including StartService, StopService, and others. The recommended way to use these services is to maintain a static property and reference the class accordingly.
Basically AES solve one problem: How do I extend the global application class, without actually replacing it, i.e. without providing a derived class?
The problem here is that many libraries need some kind of global anchor and in the past, tool developers often chose to provide this by subclassing the next available central artifact (e.g. the page class or the application). And the next tool developer doing the same rendered those two libraries mutually exclusive, just by employing an adverse implementation strategy.
However, SL3 solves only the providing part of the equation, the part related to the application class and the instantiation of the AES. But look again at the consumer code:
Following the recommendations, I get singleton access to some object. Worse, the calling code is directly and tightly coupled to the AES class. But frankly, do I need SL3 to implement a singleton? Certainly not. Then why use AES in the first place?
What I’d love is to have the consumer code depend on some service (read interface), not on the actual implementation. And the ability to swap those services in and out, without affecting the caller. Matter of fact, the calling code doesn’t and shouldn’t care whether I use a SecurityServiceThatGetsItsInformationFromTheServer or a SecurityServiceThatGetsItsInformationFromWindowsAzure (or google account, open id, whatever).
If I could get that, AES would become a very valuable feature… .
Introducing Service Providers
OK, what the calling code needs is some service in terms of a contract, say ISecurityService, that it can ask for. And the same is true for any crosscutting concern, such as error reporting, tracing, caching, you name it. And actually .NET has already addressed this need with the service provider pattern. This pattern has been used for example in WF (e.g. to introduce workflow instance persistence), and quite extensively in the Visual Studio design time infrastructure.
OK, I can hear you crying out DI. And the chorus chanting Unity or Ninject (to name just two that support SL). But think again. Would you (or rather a library developer) mandate a specific DI container without reason? And you can always wire your pet container into this pattern by providing a IFactory service using whatever container you like. After all, the pattern handles access to services, not instantiation of them. (Which is what AES does, but again, Microsoft chose not to provide a fully fledged DI container…)
What do I need to make this approach tick? I need a GetService method that iterates all AES – they are available via Application.ApplicationLifetimeObjects. And I need something to attach this method to. Using an extension method I can actually attach it to the application class:
The method iterates all AES, checks whether one implements the requested (interface) type. It also checks whether any AES itself follows the pattern and implements IServiceProvider, and respectively forwards the request if it does. (This allows me to build up a chain of providers.)
That’s it. Long talk, short implementation, all set 😉
Reimplementing the SecurityService
Now I need the rework the SecurityService and the calling code to comply with the pattern. The service interface is simple enough:
And my revised security service looks like this:
The base class ApplicationServiceBase implements the two interfaces with respective empty virtual methods, thus I only had to overwrite StartService. Aside from some reformatting the most notable difference with the previous implementation is the absence of the static Current property to access the service instance at runtime. I also renamed the class to reflect what it does. Since the calling code won’t refer to that name any more, this is now feasible.
Accessing the service at runtime is done – drum roll please – using the service provider pattern. This (admittedly not exactly nice) code can be hidden in a simple static property, adding a little convenience. I did this with a class that provides a static property, but also allows calling an extension method on the Application class:
BTW: Wouldn’t it be nice to have extension properties that I could attach to the application class…?
And the calling code, i.e. the access to the user object changes to:
Now the calling code is completely decoupled from the actual implementation of the security service, meaning I could replace it without affecting the calling code.
A look ahead: Building on the pattern
There are several use cases for this pattern that I will employ (they may or may not be addressed in later posts, but at least this list should give you some ideas):
- Last chance exception handling: A service will react to the unhandled exception event and gather context information. It will then use another service to report the error.
- Message boxes: I will need message boxes for errors, information, and confirmation. The system MessageBox is however somewhat dull. A service will cover that and a later implementation will replace the boring system dialogs with nice and shiny replacements.
- Logging and tracing: At some point I will have to tackle these demands.
I’m sure you can think of other examples, like navigation with parameter passing, global state, caching, …. Anyway, I think this is motivation enough to roll out some additional infrastructure.
As a side note: Actually I had a completely homegrown implementation of this pattern for SL2. When SL3 came out and offered AES I quickly jumped on the bandwagon and threw away most of that code. If only Microsoft had not stopped one step short of my needs… . I’d rather have the platform support that out-of-the-box. And given the simplicity of the remaining implementation this shouldn’t have been too much of an issue.
That’s all for now folks,