Runtime selection of single part?

Feb 10, 2009 at 1:17 PM
Hi all,

Can anyone tell me an easy way to intercept the composition and do this:

I'm currently providing a service via MEF that's consumed something like this:

        [ImportingConstructor]
        public DoSomethingPresenter(IDoSomethingService doSomethingService)

So I'll have an [Export(typeof(IDoSomethingService))] DoThisService : IDoSomethingService and an [Export(typeof(IDoSomethingService))] DoThatService : IDoSomethingService



I'm going to need to provide the user (of a CompositeWPF app) the ability to choose between whatever services are available at runtime, ie. there will ultimately only one IDoSomethingService to provide to the presenter above (ie. I dont want a property [Import] IEnumerable<IDoSomethingService>) that's used for the rest of the lifetime of the app (or if they can ever go back and change their mind it'll be recomposed from scratch).

Is this possible or am I stretching the bounds of MEF?

Cheers,
Andy




Feb 10, 2009 at 4:00 PM
Visual Editor does something similar - they import a collection of exports on the importer side and then choose the one to use based on the user selection.

For example, something like:

[Import]
ExportCollection<ICodeColorizer, ICodeColorizerMetadata> CodeColorizers
{
     get;
     set;
}

Then:

string selectedCodeColorizer = GetSelectedCodeColorizer();

foreach (var export in CodeColorizers)
{
    if (export.MetadataView.Name == selectedCodeColorizer)
   {   // Select this one

   }
}

The other way to do this if you don't want the importer to worry about which one was selected, would be to develop your own ExportProvider (or override GetExportsCore on the container) and do the same selection in there.
Feb 10, 2009 at 4:19 PM
Edited Feb 10, 2009 at 4:21 PM

It sounds like from what you are asking, the approach David suggested of using an ExportProvider may be the best way. With an ExportProvider you can have custom logic that can filter out which service to return based on some application state, such as the Presenter the user selected.

For example:

Here is a code snippet that would illustrate how within an ExportProvider to filter based on a user's role using a local service that was injected in the constructor.

         protected override IEnumerable<Export> GetExportsCore(ImportDefinition importDefinition)
        {
            Role currentRole = RoleService.CurrentRole;
            IEnumerable<Export> exports;
            bool found = _innerProvider.TryGetExports(importDefinition, out exports);
            var filteredExports =
                exports.Where(e => (!e.Metadata.ContainsKey("Role") || currentRole.Equals(e.Metadata["Role"])));
            return filteredExports;
        }

Regards
Glenn

Feb 10, 2009 at 9:21 PM
Glenn,

I have a similar need and am trying to understand the differences between catalogs and export providers - it seems that a catalog is one type of export provider?

I'm building generation engine that can manage T4, XML literals, and can be extended to XSLT, etc. The way in which a template decides what it can support, and details available from the wrapper, such as the template parameters, are specific to the template type.

The interface will be something like

ITemplateWrapperProvider
    bool SupportsThisTemplate(string fileName);
    ITemplateWrapper CreateNew(string fileName);

I know I want to create an ExportCollection and iterate across it to ask  whether the template provider can support the template.The correct template provider can then be called to get the right template wrapper. (although I'm welcome to other ideas, this seemed a simple approach because I'm OK pairing a template wrapper provider with the template wrapper because its only purpose is to create the wrapper and the knowledge as to how support will be determined is known only within this pair).
 
I don't know the best place to put this. I can buld a static class that does this explicitly, and perhaps since I'll be itnerating through a list of templates (multiple different templates of different types could be run simultaneously), this is the best way to go. However, I'm also trying to get my head around MEF so I'm trying to understand whether the logical place for the iteration across the collection is a custom export provider. If it is a custom export provider, what is the class for the above code derived from an how is the variable _innerProvider defined?

Kathleen
Feb 11, 2009 at 7:10 AM
Edited Feb 11, 2009 at 7:28 AM

Hi Kathleen, first thanks for all your sudden interest in MEF ;-)

Short answer, Catalogs and Export Providers may appear similar though they are different. 

Catalogs discover / return only part and export definitions, which are essentially schema. ExportProviders on the other hand return Exports which can activate real instances. In the case of a catalog, the CatalogExportProvider sits on to of a catalog, queries it for PartDefinitions and ExportDefinitions, and then creates Parts and Exports from them. As part of creating the Part, it's imports are satisfied through a composition engine.

Catalogs are about discovery. If you want to define a new way for parts to be discovered, a catalog is the way. For example if you wanted to provide a way of delivering parts from a XAP file in Siverlight, then you would create a custom XAPCatalog. Another place catalogs come into play is when you define an entirely new programming model. For example, Nick Blumhardt (on our team) has a series of blog posts (http://blogs.msdn.com/nblumhardt/archive/2008/12/14/ruby-on-mef-imports-and-exports.aspx) on creating Ruby Parts which are exposed through a Ruby Catalog. You can also create filtered catalogs, that apply a filter over the part definitions and export definition s that are returned.


In the case of an ExportProvider I can use it to wrap catalogs and provide further filtering up-stream that relates to actual instances. For example I could have an EP that actually looks at instance properties at runtime and filters based on those, for example it could only return instances whose Customer property was set to Northwind.  Another place EPs are useful for example having a default policy for which logger is returned if there are multiple. ExportProviders can also talk to other stores of data that return live instances, and it can return them as exports. For example an EP can talk to an IServiceProvider, query a database, or talking to an IoC container. I have a post on this here http://blogs.msdn.com/gblock/archive/2008/12/25/using-exportprovider-to-customize-container-behavior-part-i.aspx

In terms of which to use when, the lines are a bit gray, though they are clear cases where you use one over the other.

Now on to your example:

One way you could address this is to use MEF metadata on your template wrappers. The advantage of this is you would not need an ITemplate WrapperProvider. Instead you would have concrete TemplateWrappers that each export the ITemplateWrapper contract. Each wrapper would then use our Export Metadata ([ExportMetadata] attribute) to indicate which type of extension it supports (for example). You could then query the container to find you a specific template wrapper that supports the extension. You could do this without having to instantiate the types, as yo do with the ITemplateWrapperProvider model. It also means you are reducing the number of concepts and code you need in the system.

Here's an illustration of what I mean (in code). Copy this into a new console app, add the ref to System.ComponentModel.Composition and it should work.

namespace TemplateParserExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var catalog = new AssemblyCatalog(typeof (Program).Assembly);
            var container = new CompositionContainer(catalog);
            var wrapper = LoadTemplateWrapper("T4", container);
            Console.WriteLine(wrapper.ToString());
            Console.ReadLine();
        }

        public static ITemplateWrapper LoadTemplateWrapper(string extension, CompositionContainer container)
        {
            //find the one we want to the extension
            var templateWrapperExport = container.GetExports<ITemplateWrapper, ITemplateWrapperView>().Where(e => e.MetadataView.Extension == extension).FirstOrDefault();
            //create it
            return templateWrapperExport.GetExportedObject();
        }

    }

    public interface ITemplateWrapper
    {
        object Execute(string file);
    }

    //defines a strongly typed view for the importer
    public interface ITemplateWrapperView
    {
        string Extension { get; set; }
    }

    [ExportMetadata("Extension", "T4")]
    [Export(typeof(ITemplateWrapper))]
    public class T4TemplateWrapper : ITemplateWrapper
    {
        public object Execute(string file)
        {
            //do some processing here
            return null;
        }
    }
}

If you run this, you'll see the output is "TemplateParserExample.T4TemplateWrapper". Basically what the code is doing is discovering a T4 Template parser by querying against our metadata. The [ExportMetadata] attribute is used for expressing the available metadata.

The

ITemplateWrapperView provides a strongly typed view to the importer. I use that view when I query the container, to return for me the exports for the contract ITemplateWrapper.
I then further filter that resulting query to find the one I want, by using the strongly typed view.  With this approach, if there are multiple parsers available, then ONLY a single will be instantiated.

Now onto the rest of your questions.

1. In this case (unless I am missing something) I don't think you need a custom export provider, or a custom catalog. Instead you need a way to query the container for the specific template parser, which is what i have illustrated. The only reason you might want to use a custom provider for this, is if you want to store the type of parser needed some where, and have the container transparently know which one to return. I am imagining though in this case you'd be processing potentially many files of different providers, so that probably doesn't make sense.

2. In the code above, I passed inner provider in the constructor when I created the ExportProvider itself. This is a common pattern when you want to do filtering, basically the outer provider is a decorator that filters off the inner.

Hope all of this helps.

 

 

 

Feb 11, 2009 at 12:31 PM
Glenn,

Thank you for the excellent discussion. I understand the difference between catalaogs, export providers. This also clarified a subtle and important point I had not grokked. If I understand you, the Export<T> does not contain the actual instance, but rather the capacity to create the instance. Thus we have no instance until the end of this code

            var templateWrapperExport = container.GetExports<ITemplateWrapper, ITemplateWrapperView>().Where(e => e.MetadataView.Extension == extension).FirstOrDefault();
            //create it
            return templateWrapperExport.GetExportedObject();

I might use the approach of querying MEF metadata  for the community edition of the T4 harness I'm trying to get out because an extension is probably sufficient. , I doubht I will use it internally. I want code running somewhere that can knows how to query deeper. For example, a dll extension could be at least kinds of templates - code spit (String builder), or XML literals. Perhaps I will decide I do not care, but I'll also explore some other approaches.The only pattern I'm seeing that lets me run code to make the decision without the factory is to instantiate all the wrappers and ask them, throwing away the ones we don't need. Is there a better pattern (assuming the two lines of code above cannot know the decision basis (it could be more recent, location, who knows and quite specific to the template). I am also theough considering how important that requirement is. I'd love drop a concept if its unnecessary.

If there are multiple export providers for a container? How do they work together?

Thanks again for your great post.
Kathleen