Suppose you’re building a WPF application and have a number of actions that you want to perform on application start-up. Here’s a pattern that I’ve used successfully for the last few years that works with Caliburn.Micro or other MVVM frameworks…
If you're using Caliburn.Micro, the framework provides a Bootstrapper as an entry point to your application and the overridable Configure method is a great place to initialize your application-specific services.
You might be tempted to do something like this:
protected override void Configure() { Container = new CompositionContainer( new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>())); var batch = new CompositionBatch(); // initialize Caliburn.Micro services and register with container EventAggregator = new EventAggregator(); batch.AddExportedValue<IWindowManager>(new WindowManager()); batch.AddExportedValue<IEventAggregator>(EventAggregator); batch.AddExportedValue(Container); Container.Compose(batch); // initialize and register all my custom components var myService = new MyService(); myService.Configure("config.xml"); // repeat for all other services... }
While this strategy works for one or two services, you'll find that as you add additional services your Bootstrapper logic becomes quite bloated and simply owns too many responsibilities.
To simplify things, let's define a simple interface that describes all of our application services:
public interface IApplicationService : IDispose { void Initialize(); }
And now we can generically initialize all services that implement this interface:
protected void Configure() { // snip... Container.Compose(batch); InitializeApplicationServices(); } protected void InitializeApplicationServices() { var services = Container.GetExports<IApplicationService>(); foreach (Lazy<IApplicationService> exportedService in services) { IApplicationService service = exportedService.Value; Log.InfoFormat("Initializing IApplicationService:{0}.", service.GetType().Name); service.Initialize(); } } protected override OnExit(object sender, EventArgs e) { Container.Dispose(); base.OnExit(sender, e); } [Export(typeof(IApplicationService))] public class MyApplicationService : IApplicationService { private MyService _service; public void Initialize() { _service = new MyService(); _service.Configure("config.xml"); } public void Dispose() { GC.SuppressFinialize(this); Dispose(true); } protected virtual void Dispose(bool disposing) { _service.Dispose(); } }
There! That’s a lot cleaner. Now the Bootstrapper doesn’t know anything about our services and we can add new services without having to further extend this class. There are a few additional benefits:
- If you’re using MEF, all classes that are export IApplicationService are singletons by default.
- All services have a predefined start (Initialize) and end (Dispose), thereby avoiding work that is often put in the constructor.
- Perhaps not obvious, but if you’re using MEF, calling Container.Dispose() when the application exits automatically calls Dispose on all our application services, too.
So there you have a basic initialization routine for all your background services. My next few posts will demonstrate how to really take advantage of this pattern.
P.S: Astute readers of my blog might recognize that I'm using an Initialize() method which I've called out as a bad habit on my On Notice Board. While “Init methods” is high on my list, the context here is different. In this context, the only method I know about is Initialize(), whereas the “Init method” smell is a required method that must be called before calling other methods on the same class. I’ll blog about how to remove Init methods later.
0 comments:
Post a Comment