开发者

How to import specific part from multi parts in 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 i开发者_如何学Gonterface 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


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.


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; }


You can add export metadata to distinguish the different exports from each other. Then on the import side you will need to use an ImportMany and then filter it based on the metadata to find the one you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜