The Light MEF like PostSharp Aspects

“Everything should be made as simple as possible, but no simpler.”  – Albert Einstein

Today I begin with answer the last post question. One line implementation of method

public static Arguments CreateArguments(Type t)

Is

return
  (Arguments)Activator.CreateInstance(typeof(Arguments<>).MakeGenericType(t));

Not so difficult and all of you probably guess.

Today’s subject will be the „Light MEF” and to be more specific, I would prepare two aspects that work similarly to MEF except it will be lighter and faster. Of course, it is only the beginning. I would like to show how easy writing that kind of PostSharp aspect is.

So lets begin with example implementation of very easy and light a Service Locator Object class. It should provide two methods. First, register instances of any type. And second, to get those instances. I add a default parameter for those methods with a key name of the service implementation.  It is useful when we have many implementations of the same interface.

namespace PostSharpTrainingWorkshop.Aspects.Shared
{
  using System;
  using System.Collections.Concurrent;
  using System.Collections.Generic;
  public class ServiceLocator
  {
    static ServiceLocator m_instance;
    static object m_locker = new object();
    public static ServiceLocator Instance
    {
      get
      {
        if (m_instance != null)
          return m_instance;
        lock (m_locker)
          return m_instance ?? (m_instance = new ServiceLocator());
      }
    }
    public IDictionary<string, Object> Services { get; private set; }
    private ServiceLocator()
    {
      Services = new ConcurrentDictionary<string, object>();
    }
    public bool Register(Type type, object service, string key = null)
    {
      string serviceKey;
      Services.Add((serviceKey = key ?? string.Empty + type.FullName), service);
      return Services.ContainsKey(serviceKey);
    }
    public object GetService(Type type, string key = null)
    {
      object service;
      if (Services.TryGetValue(key ?? string.Empty + type.FullName, out service))
        return service;
      return null;
    }
  }
}

As you can see it is very easy. I created a Service Locator Object class, and now I will show you two light aspects. First will be an Export aspect that registers implementations of services. And the second one will be an Import aspect that is put before registered implementation into a property. I said property, and you probably know now that I take the LocationInterceptionAspect as base one. To show only important things, I did not implement compile time and run time validation, but if you wish, you can do that and, for example, check if the property type is an interface or anything you wish.

namespace PostSharpTrainingWorkshop.Aspects
{
  using System;
  using PostSharp.Aspects;
  [Serializable]
  public class ExportAttribute : LocationInterceptionAspect
  {
    readonly string m_key;
    public ExportAttribute(string key = null)
    {
      m_key = key;
    }
    public override void OnSetValue(LocationInterceptionArgs args)
    {
      args.ProceedSetValue();
      Shared.
      ServiceLocator.
      Instance.Register(args.Location.LocationType, args.Value, m_key);
    }
  }
}
namespace PostSharpTrainingWorkshop.Aspects
{
  using System;
  using PostSharp.Aspects;
  [Serializable]
  public class ImportAttribute : LocationInterceptionAspect
  {
    readonly string m_key;
    public ImportAttribute(string key = null)
    {
      m_key = key;
    }
    public override void OnGetValue(LocationInterceptionArgs args)
    {
      args.SetNewValue(
        Shared.
        ServiceLocator.
        Instance.
        GetService(args.Location.LocationType, m_key));
      args.ProceedGetValue();
    }
  }
}

As you can see it is not big issue. And ad last, I will test my Light MEF :). First, a registration class, second a get a service class, and last, a test method.

namespace PostSharpTrainingWorkshop.Tests
{
  using Aspects;
  using ComponentA;
  using ComponentB;
  using Contracts;
  class RegisterTest
  {
    [Export("B")]
    public IIntroduceByName NameB { get; private set; }
    [Export("A")]
    public IIntroduceByName NameA { get; private set; }
    public RegisterTest()
    {
      NameA = new ClassA();
      NameB = new ClassB();
    }
  }
}
namespace PostSharpTrainingWorkshop.Tests
{
  using Aspects;
  using Contracts;
  class GetServiceTest
  {
    [Import("B")]
    public IIntroduceByName Name { get; private set; }
    public string SayName()
    {
      return Name.SayName();
    }
  }
}
[TestMethod]
public void LightMEFTestMethod()
{
  new RegisterTest();
  if(!@"My name is B".Equals(new GetServiceTest().SayName()))
    throw new Exception("Bad name.");
}

Ok, so feel free to comment this post and disuse about aspects. Maybe you have got different ideas? What do you think about that example of aspects? Are you enjoy it?

And easy question for ending. What is enough implementation of ClassA and ClassB and IIntroduceByName interface to work with my example of the test?

Best regards,

P ;).

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.