Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to import specific part from multi parts in MEF?

Tags:

mef

I am using MEF as DI container and the problem is that I want to import specific part from multiple parts.

For example, I have following codes :

public interface IService
{
    void Send();
}

[Export(typeof(IService))]
public class Service : IService
{
    public void Send()
    {
        Console.WriteLine("Service.Send");
    }
}

[Export(typeof(IService))]
public class FakeService : IService
{
    public void Send()
    {
        Console.WriteLine("FakeService.Send");
    }
}

[Import]
public IService Service { get; set; } // ---> let's say I want to use FakeService

Is there any solution?

Thanks in advance

like image 249
Ray Avatar asked Jan 06 '11 04:01

Ray


2 Answers

You can export metadata with your class, here's an example:

public interface ILogger
{
  void Log(string message);
}

[Export(typeof(ILogger)), ExportMetadata("Name", "Console")]
public class ConsoleLogger : ILogger
{
  public void Log(string message)
  {
    Console.WriteLine(message);
  }
}

[Export(typeof(ILogger)), ExportMetadata("Name", "Debug")]
public class DebugLogger : ILogger
{
  public void Log(string message)
  {
    Debug.Print(message);
  }
}

Given that contract and those example implementations, we can import out types as Lazy<T, TMetadata> whereby we can define a metadata contract:

public interface INamedMetadata
{
  string Name { get; }
}

You don't need to worry about creating an implementation of the metadata, as MEF will project any ExportMetadata attribute values as concrete implementation of TMetadata, which in our example is INamedMetadata. With the above, I can create the following example:

public class Logger
{
  [ImportMany]
  public IEnumerable<Lazy<ILogger, INamedMetadata>> Loggers { get; set; }

  public void Log(string name, string message)
  {
    var logger = GetLogger(name);
    if (logger == null)
      throw new ArgumentException("No logger exists with name = " + name);

    logger.Log(message);
  }

  private ILogger GetLogger(string name)
  {
    return Loggers
      .Where(l => l.Metadata.Name.Equals(name))
      .Select(l => l.Value)
      .FirstOrDefault();
  }
}

In that sample class, I am importing many instances, as Lazy<ILogger, INamedMetadata> instances. Using Lazy<T,TMetadata> allows us to access the metadata before accessing the value. In the example above, I'm using the name argument to select the appropriate logger to use.

If it is not right to instantiate the class on import, you can use an ExportFactory<T,TMetadata> which allows you to spin up instances of your types on demand. (ExportFactory is included in the Silverlight version of .NET 4.0, but Glenn Block did throw the source code on codeplex for Desktop/Web use.

I hope that helps.

like image 92
Matthew Abbott Avatar answered Jan 04 '23 05:01

Matthew Abbott


You can use the overload of Export that takes a contract name as well. Then import it with the contract name.

[Export("Service", typeof(IService))]
public class Service : IService {
}

[Export("FakeService", typeof(IService))]
public class FakeService : IService {
}

[Import("FakeService")]
public IService Service { get; set; }
like image 31
Josh Avatar answered Jan 04 '23 04:01

Josh