Simplified MEF Export Provider using MEF Property Exports

674ladderAll of my projects recently use MEF to handle all dependency injection in both the service layer and in the view models (PRISM).  I recently needed to be able to inject a different implementation of a particular interface to all of the imports that called for that interface.  All based on a setting or a flag. 

The Problem…

Our current project communicates with a pump module that is the main hardware unit for the application, and that uses a serial port to connect with the pc and our software. I have already written a system layer service library for talking to the pump. The main application also has a pump service that is in the application layer and that is the communication point for the system service. All of the services have an interface defined in Core.Interfaces and this is where the user interface modules can reference the application services, such as in the view models. The interfaces are implemented by classes in the Application Services library including the Pump Service interface, aptly named IPumpService.

Some Context:
Here are some links for technologies used in the project that you can use for references for this article.

As the project has proceeded there are many services that use the pump service to talk to the pump module and all of these services import the IPumpService interface into their constructors using the ImportingConstructor attribute from MEF. This makes it easy for any service that needs the pump to access it, but that also means there are many instances of the class that implements the IPumpService interface in existence and being used. Which in turn means that if I wanted to replace the current implementation of IPumpService with an alternative, let us say a pump simulation class, I have a lot of places that would need to be altered.

So that is the goal…

Find a way to quickly (no budget for a simulator so all simulator work must not eat into project time) implement a substitution class for the existing IPumpService that will allow all MEF Imports to get the PumpSimulator implementation instead of the real one, if a config setting is true.  I have all of these services that import IPumpService into their constructor. Each of these services depends upon the pump service for various boolean flags and other commands. The PumpService class implements IPumpService and is therefore instantiated by MEF and imported into the constructors of the dependent classes. I want to be able to run the application without the physical hardware and thereby speed up development and testing (we only have one prototype of the pump instrument). The simulator also implements IPumpService but is called PumpSimulator.

Some Options:

So how do I use MEF to replace all of the currently imported instance of IPumpService which is PumpService, with an alternative implementation of IPumpService, my PumpSimulator class?

Most of our MEF exports are implementations of the various service interfaces. Lets look at some code, for one of the services that we mentioned are the consumers of the IPumpService export.  The ManifoldService manages the pump manifold and must talk to the pump to perform installation and removal workflows for the manifold parts.  Here is declaration of the service and its constructor (with importing attribute in place):

   1: [Export(typeof(IManifoldService))]
   2:     public class ManifoldService : IManifoldService
   3:     {
   4:         private readonly IAlertService _alertService;
   5:  
   6:         private readonly IAuditService _auditService;
   7:  
   8:         private readonly IConsumablesService _consumablesService;
   9:  
  10:         private readonly IDataService _dataService;
  11:  
  12:         private readonly IEventAggregator _ea;
  13:  
  14:         private readonly CancellationTokenSource _lifetimeTasksCanceller = new CancellationTokenSource();
  15:  
  16:         private readonly ILogger _logger = LoggerFactory.GetLogger(LogCategory.ExecutionFlow, typeof(IManifoldService));
  17:  
  18:         private readonly IPumpService _pumpService;
  19:  
  20:         private readonly ITaskFactory _taskFactory;
  21:  
  22:         protected Task _expiredTask;
  23:  
  24:         private bool _isInstallationComplete;
  25:  
  26:         private bool _isManifoldFlushRequired;
  27:  
  28:         private Manifold _manifold;
  29:  
  30:         private ISolution _universalIngredient;
  31:  
  32:         protected Task _warningTask;
  33:  
  34:         /// <summary>
  35:         /// Initializes a new instance of the <see cref="ManifoldService" /> class.
  36:         /// </summary>
  37:         /// <param name="ea">The event aggregator instance.</param>
  38:         /// <param name="pumpService">The pump service.</param>
  39:         /// <param name="auditService">The audit service.</param>
  40:         /// <param name="alertService">The alert service.</param>
  41:         /// <param name="consumablesService">The consumables service.</param>
  42:         /// <param name="dataService">The data service.</param>
  43:         /// <param name="tasks">The tasks.</param>
  44:         [ImportingConstructor]
  45:         public ManifoldService(
  46:             IEventAggregator ea,
  47:             IPumpService pumpService,
  48:             IAuditService auditService,
  49:             IAlertService alertService,
  50:             IConsumablesService consumablesService,
  51:             IDataService dataService,
  52:             ITaskFactory tasks)
  53:         {
  54:             _auditService = auditService;
  55:             _alertService = alertService;
  56:             _pumpService = pumpService;
  57:             _consumablesService = consumablesService;
  58:             _dataService = dataService;
  59:             _taskFactory = tasks;
  60:  
  61:             _ea = ea;
  62:             _ea.GetEvent<ApplicationStartedEvent>().Subscribe(OnApplicationStarted);
  63:             _ea.GetEvent<ApplicationShuttingDownEvent>().Subscribe(OnApplicationShutdown);
  64:             _ea.GetEvent<PrimeCompletedEvent>().Subscribe(SetManifoldFlushRequired);
  65:             _ea.GetEvent<DispenseStoppedEvent>().Subscribe(OnDispenseStopped);
  66:             _ea.GetEvent<UniversalIngredientChangedEvent>().Subscribe(CheckForUIChange);
  67:             _ea.GetEvent<TransferSetChangedEvent>().Subscribe(ts => CheckForUIChange(ts.UiStationNumber));
  68:         }
  69:  
  70:         // ...
  71: }

As you can see all of the dependencies, including our IPumpService are imported using MEF’s ImportingConstructor and this technique is used throughout the project.  The constructor simply assigns local private holders for the services and wires up some aggregated events.  The key point here is the import of the IPumpService as a ctor parameter, and how I can replace it with an alternate implementation of IPumpService (my PumpSimulator class) without putting code in production that is like my first solution below.

My first solution…

Was not elegant and certainly not ideal.  It was originally just temporary fix I used to be able to run the manifold service workflows without being connected to the pump.  The real IPumpService implementation requires a connection to the hardware to function, and I did not want to alter that.  So I simply added code to the Manifold Service constructor to hijack the local private holder for IPumpService.  Like this:

   1: [ImportingConstructor]
   2: public ManifoldService(
   3:     IEventAggregator ea,
   4:     IPumpService pumpService,
   5:     IAuditService auditService,
   6:     IAlertService alertService,
   7:     IConsumablesService consumablesService,
   8:     IDataService dataService,
   9:     ITaskFactory tasks)
  10: {
  11:     _auditService = auditService;
  12:     _alertService = alertService;
  13:     _pumpService = pumpService;
  14:     _consumablesService = consumablesService;
  15:     _dataService = dataService;
  16:     _taskFactory = tasks;
  17:  
  18:     _ea = ea;
  19:     _ea.GetEvent<ApplicationStartedEvent>().Subscribe(OnApplicationStarted);
  20:     _ea.GetEvent<ApplicationShuttingDownEvent>().Subscribe(OnApplicationShutdown);
  21:     _ea.GetEvent<PrimeCompletedEvent>().Subscribe(SetManifoldFlushRequired);
  22:     _ea.GetEvent<DispenseStoppedEvent>().Subscribe(OnDispenseStopped);
  23:     _ea.GetEvent<UniversalIngredientChangedEvent>().Subscribe(CheckForUIChange);
  24:     _ea.GetEvent<TransferSetChangedEvent>().Subscribe(ts => CheckForUIChange(ts.UiStationNumber));
  25:  
  26: #if DEBUG
  27:             if (Constants.UsePumpSimulator)
  28:             {
  29:                 _pumpService = ServiceLocator.Current.GetInstance<IPumpService>(Constants.Simulator);
  30:             }
  31: #endif
  32:  
  33: }

In lines 26 –31 I have simply added some code to reassign the _pumpService private variable to hold an instance of the PumpSimulator.  Again, both the PumpService and the PumpSimulator classes implement IPumpService so this was not a problem to make the assignment, ugly as it may be.  And it worked, temporarily.

The issue quickly arose again however when I needed another service to use the simulator also.  I was faced with the choice of continuing to alter the constructors, knowing I would have to remove this code before the next release, or come up with something better.  To Google!

Some quick Google research led me to MEF Export Providers, however I had very little time to solve this problem, less that it would have taken for me to implement a custom Export Provider.  I would like to stress that my eventual solution may not be “best practice”, it is however simple and it works and violates no major points that I am aware of.  But, that said, I would also like to emphasize that an ideal solution, given the time and budget to implement it, would be a custom export provider that could use whatever conditions I would like to load the correct IPumpService implementation when asked for it by the catalog. 

One other reason why I did not want to use a custom export provider at this point was the fact that you have to pass the custom provider implementation to the catalog which in my case, using Prism, is in another project in the Bootstrapper.cs file.  I wanted to only make a simple alteration with as small a footprint as possible to the Application Services layer which is its own class library.  I did not particularly want to alter the main executable which is where Bootstrapper.cs lives and is where the MEF catalog is constructed.  Our current architecture requires no references to the application services library by project upstream, such as the main executable or the UI modules.  These projects all reference Core and Core.Interfaces which is where all of the service interfaces (IPumpService) live.  Using a custom provider at the level of the bootstrapper may have resulted in me having to reference the implementations of IPumpService directly (to construct them for export) and I did not want to do that.

Simplified Custom MEF Export Provider

That is when I found this information (https://mef.codeplex.com/wikipage?title=Declaring%20Exports)  about using properties to export.  This lead me to figure out a simple way of alternating my IPumpService export between the two implementations. Using a property export meant I now had a way to get both of my implementations together into a class and then choose the exported instance based on a config setting.

I had used property imports before, usually to load service lazily in order to avoid cyclic dependency problems, but I was not aware you could use a property to export  and thereby satisfy other imports.  Basically, you can use the Export attribute on a property of the type you need to use to satisfy an import elsewhere, such as the importing constructor of my pump dependent services, such as Manifold service we have already seen.

First I got rid of my pump simulator related constants and created a new setting in the application’s app.config file (I used the main exe config file, but in retrospect I could have used an app.config added to the Application Services project to further tighten the encapsulation at the service layer), called “UseSimulator” with a value of “true”. 

I then created a small class called PumpServiceProvider that used the importing constructor attribute to import both PumpService and PumpSimulator. I changed both of their class declarations to use a plain Export attribute instead of exporting typeof(IPumpService). So now I had no IPumpService export but I did have exports for PumpService and PumpSimulator, both of which I imported into my new provider class.

PumpServiceProvider Class:

   1: [Export]
   2: public class PumpServiceProvider
   3: {
   4:     private readonly ILogger Logger = LoggerFactory.GetLogger(LogCategory.ExecutionFlow);
   5:  
   6:     private readonly bool _useSimulator;
   7:  
   8:     public PumpServiceFactory()
   9:     {
  10:         if (!bool.TryParse(ConfigurationManager.AppSettings["UseSimulator"], out _useSimulator))
  11:         {
  12:             Logger.Log(LogLevel.Error, "Failed to parse config setting for UseSimulator.");
  13:             _useSimulator = false;
  14:         }    
  15:     }
  16: }

The constructor simply loads the app configuration setting used to determine whether to use the simulator implementation of IPumpService or the real implementation. 

I then added a single property to my PumpServiceProvider class called ActivePumpService that was of type IPumpService and had an export tag like this:

   1: [Export(typeof(IPumpService))]
   2: public IPumpService ActivePumpService
   3: {
   4:     get
   5:     {
   6:         return _useSimulator ? PumpSimulator as IPumpService : PumpService;
   7:     }
   8: }

This property satisfies all Imports of IPumpService throughout the application, because I removed the Export attribute from the two implementations of IPumpService and only have this sole Export of type IPumpService.  I then added two more properties to the class, one for each of the types of pump service implementation.  As such:

   1:  
   2: [Import]
   3: private PumpService PumpService { get; set; }
   4:  
   5: [Import]
   6: private PumpServiceSimulator PumpSimulator { get; set; }
   7:  

These are used in the Get statement of the ActivePumpService property above, based on whether or not the useSimulator flag is true.  And that did it.  It actually worked the first time I tried it, which is always really cool.  I had a very low impact, small footprint solution to my simulator injection problem. 

Conclusion

The most important advantage of this solution in the end, was the fact that it involved no alterations to the dozen or so classes that are consumers of the IPumpService interface.  I was able to accomplish my goal as stated at the beginning of the post, and provide an alternate simulator implementation for my pump service with only minimal additions in the form of my PumpServiceProvider class.

Here is the complete version of the provider class:

   1: [Export]
   2: public class PumpServiceProvider
   3: {
   4:     private readonly ILogger Logger = LoggerFactory.GetLogger(LogCategory.ExecutionFlow);
   5:  
   6:     private readonly bool _useSimulator;
   7:  
   8:     public PumpServiceFactory()
   9:     {
  10:         if (!bool.TryParse(ConfigurationManager.AppSettings["UseSimulator"], out _useSimulator))
  11:         {
  12:             Logger.Log(LogLevel.Error, "Failed to parse config setting for UseSimulator.");
  13:             _useSimulator = false;
  14:         }    
  15:     }
  16:  
  17:     [Import]
  18:     private PumpService PumpService { get; set; }
  19:  
  20:     [Import]
  21:     private PumpServiceSimulator PumpSimulator { get; set; }
  22:  
  23:     [Export(typeof(IPumpService))]
  24:     public IPumpService ActivePumpService
  25:     {
  26:         get
  27:         {
  28:             return _useSimulator ? PumpSimulator as IPumpService : PumpService;
  29:         }
  30:     }
  31: }
 
and only 30 lines to provide all of my IPumpService consumers with a simulator!  MEF saves the day.