Using GetExports

Feb 1, 2011 at 1:24 PM

Hi,

I have scenario in which I need to create a concrete implementation of an interface based on type. Say I have "IView" (interface) <- View (concrete implementation/Class). I need to create a object of "View" class if my input is typeof(IView).

So far, I have used,

             // ViewType is Typeof(IView)

              IEnumerable<Export> matching = container.GetExports(
                                            new ContractBasedImportDefinition(AttributedModelServices.GetContractName(viewtype),
                                                                              AttributedModelServices.GetTypeIdentity(viewtype),
                                                                              null, ImportCardinality.ExactlyOne,
                                                                              false, false, CreationPolicy.Any));
            // This calls the constructor of the specific concrete implementation of the contract
            var viewObject = matching.Single().Value;

 

This implementation is perfectly fine, if I have single concrete implementation of IView. What if I have multiple Implementation of IView?

How would GetExports(...) would know which concrete implementation to return based on Type object.

 

Any help/pointers are highly appreciated.

Thanks in advance.

Indro

Feb 3, 2011 at 7:56 AM

If I understand you correctly, you want to pick an instance amoung interfaces (since the GetExports method already returns a enumerable collection).

Unless you specify specific contract names for these specific exports, MEF can't help you with that. Though you can export metadata or define public properties that help you identify the correct choice.

Given the IView type, MEF autoamtically infers the contract name from the type IView, however, the contract name can be changed to whatever you want and MEF still tracks the type identity (which ensures some kind of type safety).

If you export 3 different views with 3 different contract names you can query all these exports by just asking for all IView exports or you can ask for a specific view by asking for all IView exports with a specific contract.

This code hopefully shows how contract names can be used to select/filter specific imports.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

public interface IView
{
}

[Export("A", typeof(IView))]
class A : IView
{
    public override string ToString()
    {
        return "A";
    }
}

[Export("B", typeof(IView))]
class B : IView
{
    public override string ToString()
    {
        return "B";
    }
}

class Program
{
    static void Main(string[] args)
    {
        var catalog = new TypeCatalog(typeof(A), typeof(B));
            
        var container = new CompositionContainer(catalog);

        Console.WriteLine("container.GetExports<IView>():");

        // A bit surprising, but this actually doesn't return
        // any views becuase it is using the default contract name
        foreach (var view in container.GetExports<IView>())
        {
            Console.WriteLine(view.Value);
        }
            
        Console.WriteLine();

        Console.WriteLine("container.GetExports<IView>(\"A\"):");

        foreach (var view in container.GetExports<IView>("A"))
        {
            Console.WriteLine(view.Value);
        }

        Console.WriteLine();

        Console.WriteLine("container.GetExports<IView>(\"B\"):");

        foreach (var view in container.GetExports<IView>("B"))
        {
            Console.WriteLine(view.Value);
        }

        Console.WriteLine();
    }
}

Feb 3, 2011 at 12:51 PM

Hi Leidegre,

Thanks. Looks fine to me. A lot of thought flying on my head after seeing this snippet.

I will put my thoughts into another thread:D

 

best regards,

Indro

Jul 20, 2012 at 3:23 PM

The sample above gave me an idea! thx! My plugins will work this way (see below):

 

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System;

public class Program
{
	public static void Main(string[] args)
	{
		Program p = new Program();
		p.Run();
	}

	public CompositionContainer container = null;

	/// <summary>
	/// Set the container global variable
	/// </summary>
	private void Compose()
	{
		AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

		container = new CompositionContainer(catalog);
	}

	public void Run()
	{
		Compose();

		// Calling a method without use interfaces. More flexible and "easy to use".
		// Useful for plugins
		foreach (var view in container.GetExports<Func<string, bool>>("MessageSender"))
		{
			Console.WriteLine(view.Value("PUC-Campinas"));
		}

		Console.ReadKey();
	}
}

public class MessageSender
{
	[Export("MessageSender")]
	public bool Send(string message)
	{
		Console.WriteLine(message);
		return true;
	}
}