Microsoft.Composition (Portable MEF): Dependency Injection and Service Locator

2016/02/17

Microsoft.Composition (Portable MEF):  Dependency Injection and Service Locator via Convention

This post is part of mini series, which complete the full MEF series.

this post will go over Microsoft.Composition Dependency Injection and Service Locator using conventions.

in the previous post I describe hot to register types using convention, but in real world types often has

their own dependencies which should be satisfies (this is why the IoC process also called composition).

In order to satisfy those dependencies you can take different approaches:

Dependency Injection

constructor-based injection

The most common way of passing the dependencies is  constructor-based injection where the dependencies

passes to the constructor. this common practice accepted by most IoC (including MEF).

Using this approach it’s quite easy to figure out which dependencies each component has.

It’s also easy to test the component without having to set-up any IoC framework.

The down-side of is that no mutual dependencies allowed.

even though mutual dependencies may not consider as best practice in most cases, when you compose

the component right, it can be quite powerful capability, for now, just consider a Logger that have to

read its setting before it become operational and the Setting that may have to write some log

(I will explain how to do it right on future post).

constructor base injection looks like this:

Code Snippet
  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5.     var conventions = new ConventionBuilder();
  6.     conventions.ForTypesDerivedFrom<ILogger>()
  7.         .Export<ILogger>()
  8.         .Shared();
  9.     conventions.ForTypesDerivedFrom<ISetting>()
  10.         .Export<ISetting>()
  11.         .Shared();
  12.     conventions.ForType<Logic>()
  13.         .Export()
  14.         .Shared();
  15.  
  16.     var configuration = new ContainerConfiguration()
  17.             .WithAssembly(typeof(Program).Assembly, conventions);
  18.  
  19.     using (var container = configuration.CreateContainer())
  20.     {
  21.         var logic = container.GetExport<Logic>();
  22.         // the rest of the code …
  23.     }
  24. }
  25.  
  26. public class Logic
  27. {
  28.     private readonly ILogger _logger;
  29.     private readonly ISetting _setting;
  30.  
  31.     public Logic(
  32.         ILogger logger,
  33.         ISetting setting)
  34.     {
  35.         _logger = logger;
  36.         _setting = setting;
  37.     }
  38. }

Setter Injection

Setter injection is less popular and not supported on many IoC framework

but it’s easy to use, can support mutual dependencies (depend on the IoC framework, MEF do support it).

on some framework the setter must use public modifier which is kind of sucks,

but MEF does support private setters (Unfortunately this version of MEF[Microsoft.Composition]

don’t support private modifier so well).

this approach is native to MEF either by [Import] attribute or by  convention.

the following code demonstrate the convention style.

Code Snippet
  1. static void Main(string[] args)
  2. {
  3.     var conventions = new ConventionBuilder();
  4.     conventions.ForTypesDerivedFrom<ILogger>()
  5.         .Export<ILogger>()
  6.         .Shared();
  7.     conventions.ForTypesDerivedFrom<ISetting>()
  8.         .Export<ISetting>()
  9.         .Shared();
  10.     conventions.ForType<Manager>()
  11.         .Export()
  12.         .ImportProperties<ILogger>(p => p.Name == "Logger")
  13.         .ImportProperties<ISetting>(p => p.Name == "Setting")
  14.         .Shared();
  15.  
  16.     var configuration = new ContainerConfiguration()
  17.             .WithAssembly(typeof(Program).Assembly, conventions);
  18.  
  19.     using (var container = configuration.CreateContainer())
  20.     {
  21.         var manager = container.GetExport<Manager>();
  22.         // the rest of the code …
  23.     }
  24. }
  25.  
  26. public class Manager
  27. {
  28.     public ILogger Logger { get; set; }
  29.     public ISetting Setting { get; set; }
  30.  
  31.     // …
  32. }

personally I don’t like registering property by convention (without attribute), because:

a: it is too much work

b: you should be familiar with the class dependencies at the configuration level and it’s getting

    out of sync really fast (after few refactoring). it is hard to maintain and seem to be the wrong 

    responsibility of the configuration.

because this version of MEF don’t handle composition private property so well (in some cases)

you can consider the following pattern in order to hide the dependencies from intellisence (kind of a trick):

Code Snippet
  1. public class Logic
  2. {
  3.     [Import]
  4.     [EditorBrowsable(EditorBrowsableState.Never)]
  5.     public ILogger Logger { get; set; }
  6.     [Import]
  7.     [EditorBrowsable(EditorBrowsableState.Never)]
  8.     public ISetting Setting { get; set; }
  9. }

The [EditorBrowsable(EditorBrowsableState.Never)] attribute will hide the property from intellisense

and you will get experience like with private modifier.

Service Locator

Another common approach is Service Locator. I believe that there is to much confusion between

Dependency Injection and Service Locator so I will share my view on this issue.

Service Locator is equals to Dependency Injection with additional level of abstraction.

while the Dependency Injection’s constructor is getting all dependencies,

the Service locator will get single parameter which can be use to resolve the dependencies.

the following snippet show the differences:

Code Snippet
  1. public class Logic
  2. {
  3.     private readonly ILogger _logger;
  4.     private readonly ISetting _setting;
  5.  
  6.     // Dependency Injection
  7.     public Logic(ILogger logger, ISetting setting)
  8.     {
  9.         _logger = logger;
  10.         _setting = setting;
  11.     }
  12.  
  13.     // Service Locator
  14.     public Logic(CompositionContext locator)
  15.     {
  16.         _logger = locator.GetExport<ILogger>();
  17.         _setting = locator.GetExport<ISetting>();
  18.     }
  19. }

As you can see you can achieve the same functionality using both patterns,

yet there’re so many discussion of which pattern is better.

I will try to help with this decision by going over the strength and weakness of each pattern.

 

Dependencies injection strength and weakness:

Starting with the Dependencies injection, it is very straight forward and descriptive.

– you can figure the class dependencies from it’s signature.

testing don’t need the IoC framework in order to satisfy the dependencies:

– the down side is that you may have classes with to many parameters

  and maintainability may also be an issue (adding or removing class dependency may lead to

  changes on derived class’s constructor and potentially break-down  of many tests which

  you should fix one by one) .

 

Service Locator strength and weakness:

– the constrictor always having single parameter and it much easier to apply new

   class dependencies (easier maintainace).

– the down-side of it is that the signature don’t reflect the dependencies.

 

Which pattern to use?

Choosing the IoC pattern is very much matter of personal preference and I don’t think that

you can argue that one pattern is better than the other.

I’m usually using the  Service Locator pattern, but for better usability I’m using extension method.

Extension methods can help with dependencies discoverability when using intellisense.

This is a common practice which also used by ASP.NET:

Code Snippet
  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3.     loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  4.     loggerFactory.AddDebug();
  5.  
  6.  
  7.     app.UseIISPlatformHandler();
  8.  
  9.     app.UseApplicationInsightsRequestTelemetry();
  10.  
  11.     app.UseApplicationInsightsExceptionTelemetry();
  12.  
  13.     app.UseStaticFiles();
  14.  
  15.     app.UseMvc();
  16. }

Everything starts with Use… is simply extension method, which define by ASP.NET (or extensions for ASP.NET).

Code Snippet
  1. public static class MvcApplicationBuilderExtensions
  2. {
  3.     public static IApplicationBuilder UseMvc(
  4.         this IApplicationBuilder app);
  5.     public static IApplicationBuilder UseMvc(
  6.         this IApplicationBuilder app,
  7.         Action<IRouteBuilder> configureRoutes);
  8.     public static IApplicationBuilder UseMvcWithDefaultRoute(
  9.         this IApplicationBuilder app);
  10. }

 

This is how you do it with Service Locator over MEF.

Code Snippet
  1. public static class Extensions
  2. {
  3.     public static ILogger ResolveLogger(
  4.         this CompositionContext locator)
  5.     {
  6.         return locator.GetExport<ILogger>();
  7.     }
  8.     public static ISetting ResolveSetting(
  9.         this CompositionContext locator)
  10.     {
  11.         return locator.GetExport<ISetting>();
  12.     }
  13. }
  14.  
  15. public class Logic
  16. {
  17.     private readonly ILogger _logger;
  18.     private readonly ISetting _setting;
  19.  
  20.     // Service Locator (with extension method)
  21.     public Logic(CompositionContext locator)
  22.     {
  23.         _logger = locator.ResolveLogger();
  24.         _setting = locator.ResolveSetting();
  25.     }
  26.  
  27.     // Service Locator (without extension method)
  28.     public Logic(CompositionContext locator)
  29.     {
  30.         _logger = locator.GetExport<ILogger>();
  31.         _setting = locator.GetExport<ISetting>();
  32.     }
  33.  
  34.     // Dependency Injection
  35.     public Logic(ILogger logger, ISetting setting)
  36.     {
  37.         _logger = logger;
  38.         _setting = setting;
  39.     }
  40. }

the constructors at lines 21 and 28 is having the same parameters (therefore won’t compile)

in reality you have to choose which pattern you like like best.

I think that choosing single pattern is better for reducing confusion and maintain code consistency.

 

Summary

this post spoke about IoC pattern more than it was about MEF.

I was using MEF on the Service Locator pattern by using CompositionContext

which is something that MEF register by default (you don’t have to manually

register it), but you can do it with any other IoC quite easily.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*