Accessing imported parts from static methods

Jan 4, 2010 at 6:11 PM
Edited Jan 4, 2010 at 6:12 PM

For starters, I am using VS 2010 with .NET Framework 4 (both Beta 2).  What I am attempting to do is apply the use of MEF to an existing application framework.  This is an exploratory effort to see if we will be able to utilize MEF to replace an existing factory pattern usage and remove (or highly reduce) our strongly coupled interdependencies.  Our framework consists of a number of service classes that provide much of the plumbing use for building applications.  Think exception logging, security access, configuration storage/retrieval, publisher/subscriber event model, etc.  In most cases, the functionality has been specialized for our environment.  At the moment, these services are exposed via a factory pattern; we are replacing the use of the factories with MEF.

The problem I've run into is an inability to access a composed part from within a static method.  I have found posts that discuss the removal of support for composing static properties or fields.  That's not exactly my issue (though if that support were still in place it would solve this issue).  I've run into this issue in several different situations, though all surrounding access from static methods.  I'll present one which will hopefully serve as a sufficient example.

Consider an extension method that needs access to one of the framework services (aka managers).  Currently, we create an instance via the appropriate factory method.  The original code (before applying MEF) might look something like the following.  This code creates an instance of the Help Manager service, which is utilized to display control specific help to the user.  (The details have been left out for brevity.)

        public static class ControlExtentionMethods
        {
            public static void ShowHelp(this Control control)
            {
                IHelpManager helpManager = ManagerFactory.GetHelpManager();

                if (helpManager != null)
                {
                    helpManager.ShowHelp(control.GetHelpKey(), control.GetHelpTopicId());
                }
            }
        }

In trying to apply MEF, I have modified the service class to be exported with the IHelpManager interface.  This export does properly works.  (I can import it via my unit testing.)  However, I am having trouble finding a way to import an IHelpManager instance for use by the static method.  Being an extension method, the method and class must be marked as static.  MEF seems to hate the use of static in any fashion.

I'm hoping some of you long time MEF developers will be able to point me toward a solution.  And I'm hoping that "don't use statics" isn't the answer.  That would probably eliminate the use of MEF for us for the application.

Thanks in advance for any and all advice.
David.

Jan 5, 2010 at 5:08 PM

Hi David

In general we do discourage the use of statics when using MEF. That doesn't mean you can't do it, however when you use MEF it leans you away from it. There are many reasons for this including it makes the code hard to reuse and difficult to test. We do allow static exports but we do not allow static imports.

In cases such as yours where you have an exsiting framework that consists of static apis there are other options. You can still use MEF by querying it's container instead of importing.

The way I would recommend this would be to wrap your ManagerFactory in our CompositionContainer which it gets in it's constructor. Methods like GetHelpManager will then delegate to the inner container by calling GetExportedValue<T> where T is the service interface.

Then you can expose ManagerFactory as a static member which your existing codebase can use.

Thanks

Glenn

Jan 5, 2010 at 5:34 PM
Edited Jan 5, 2010 at 5:37 PM

David

There is an alternative approach which will actually allow you to use imports, even though your extension method are static.

Instead of ManagerFactory directly invoking the container's GetExportedValue methods, it can delegate to a part which imports the services that the different methods will need.

For example:

public static class ControlExtensionMethods {
  private static ControlService _controlService;

  static ControlExtensionMethods() {
    var container = new CompositionContainer(...)
    _controlService = container.GetExportedValue<IControlService>();    
  }

  //use for testing
  internal static void SetControlService(IControlService controlService) {
    _controlService = controlService;
  }

  public static void ShowHelp(this Control control)
  {
     return _controlService.ShowHelp(control);
  }
}

[Export(typeof(IControlService))]
public class ControlService : IControlService {

  [Import]
  IHelperManager HelpManager {get;set;}
  
  public void ShowHelp(this Control control) {
    helpManager.ShowHelp(control.GetHelpKey(), control.GetHelpTpoicId());
  }
}

In the code you can see that ControlExtensionMethods privately creates a container and pulls an IControlService. It then delegates all calls through that service.

I've also added an internal SetControlService method which allows overrding the control service with a mock. You can use this method for mocking out the extension methods for unit testing callers of the methods. To use the SetControlService method in tests, you'll need to make sure you'll need to make your tests a friend assembly.

Advantages of this approach over the former, is services are declaratively imported, and both the extension methods and classes calling them are less brittle and eaiser to test. Disadvantage is it's a bit more work and requires you to keep the code in synch.

You can find out more about testing extension methods in Daniel Cazzulino's post here. http://www.clariusconsulting.net/blogs/kzu/archive/2009/12/22/Howtomockextensionmethods.aspx

Glenn