Design patterns: choosing the right handler depending on data type
Sorry this took so long to post, it got a lot longer then I expected it to be.

Since you are already programming off of interfaces, implementing the MEF framework should be relatively simple, provide the expansion that you need, and allow you to add in new IDocumentProcessor classes without recompiling the entire program. You may/will need to modify some aspects of your code, but overall it will remain unchanged.

MEF works by importing and exporting classes based upon interfaces and metadata tied to those classes. You can think of it like the children toys where you put the round blocks in the round holes, the square blocks in the square holes, etc (Note that is overly simplified).

Using your case for example, you would want to export your:

public class DocumentAProcessor:IDocumentProcessor
{
    ....
}


To do so, you would need to add the following to your code file:

[Export(typeof(IDocumentProcessor))]
public class DocumentAProcessor:IDocumentProcessor
{
    ....
}


The Export attribute means that you want to export that class and import it into a variable marked with the Import attribute (I'll get to that in a second). The typeof(IDocumentProcessor) informs what type you are exporting so that it can be matched with an Import expecting that same type (round peg in a round hole).

Additionaly, you can add MetadataAttributes to the classes that you are exporting that give additional information about the class. To do so will take a little more work then adding the export class but taking your example, we would want to create something similar to:

public interface IDocumentProcessorMetadata
{
    string DocumentType { get; }
}

[MetadataAttribute]
public class DocumentProcessorMetadata : Attribute, IDocumentProcessorMetadata
{
    public DocumentProcessorMetadata(string documentType)
    {
        DocumentType = documentType;
    }

    public string DocumentType { get; set; }
}


This will create a simple Metadata attribute that you can attach to your class that will contain the DocumentType that the class processes. You can have this metadata attribute hold any number of items simply by adding new properties to IDocumentProcessorMetadata and DocumentProcessorMetadata. In my system for example, I use this to hold a GUID (has to be as a string), a simple name that can be used for lookups and flags for various things. To use the DocumentProcessorMetadata attribute, you would modify your class(es) to look like:

[Export(typeof(IDocumentProcessor))]
[DocumentProcessorMetadata("DocumentType1")]
public class DocumentAProcessor:IDocumentProcessor
{
    ....
}

[Export(typeof(IDocumentProcessor))]
[DocumentProcessorMetadata("DocumentType2")]
public class DocumentBProcessor:IDocumentProcessor
{
    ....
}


That is it for the Export, now for the Import. The import works in pretty much the same way as the export. You put an [ImportMany(typeof(IDocumentProcessor))] attribute above the variable that the Exports will import into, and that is pretty much it (The ImportMany attribute imports more than 1 instance of the class, while the Import attribute will only import the first instance that is encountered). However, you will need to import to an IEnumberable&lt;Lazy&lt;IDocumentProcessor,IDocumentProcessorMetadata&gt;&gt; variable instead of the Dictionary&lt;string,IDocumentProcessor&gt; that you were using before. You can keep the dictionary that you had, but you will need to populate it another way (I'll provide an example later). What the Lazy&lt;T1,T2&gt; class allows is for the classes to be imported into the variable, but not actually created until they are needed. That way, if you have 5 document processors but in the life of the program you only ever use 2, you save the overhead of creating those 3 that you never used. That said, your import would look like this:

[ImportMany(typeof(IDocumentProcessor))]
IEnumberable&lt;Lazy&lt;IDocumentProcessor,IDocumentProcessorMetadata&gt;&gt; _processors;


To get the processor that you need, you would iterate through the _processors (or you could use linq) to find the one that handles the correct Document.Type:

public void Handle(Document document)
{
    IDocumentProcessor handler;
    //note I keep the dictionary
    //if you use TryGetValue, it is only one trip to the dictionary instead of two
    Handlers.TryGetValue(document.Type, out handler);

    if(handler == null)
    {
        foreach(var processor in _processors)
        {
            if(processor.Metadata.DocumentType == document.Type)
            {
                handler = processor.Value;
                //add the processor to the dictionary so it can be easily found later
                Handlers.Add(document.Type, processor.Value);
                break;
            }
        }
        //If it gets to this point, the processor was not found and an exception should be thrown
        throw new Exception();
    }
    Task task = Task.Factory.StartNew( () =&gt; handler.Process(document) );
}


The last piece of the puzzle is the code that will actually connect everything together. I usually have this in it's own class, in the core dll of whatever application is using it. Essentially, this class handles what is called a CompositionContainer that allows all the Exports to be tied to the Imports. You add what are called AssemblyCatelogs that contain a single assembly or you can add a DirectoryCatelog that will include any files in the directory. In your case, I would use an AssemblyCatelog so include all of your Imports (this is assuming that they are in the same assembly as the code containing the CompositionContainer) and a single DirectoryCatelog that will hold all of the IDocumentProcessor classes. If everything is in the same assembly, you can get away with just the AssemblyCatelog but I would still include the DirectoryCatelog incase you wanted to add new IDocumentProcessor classes later. The class would look something like this:

public class MEFManager
{
    private CompositionContainer _container;

    public MEFManager()
    {
        //AggregateCatalog allows multiple catalogs to be added to the CompositionContainer
        var catalog = new AggregateCatalog(); 
        catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
        catalog.Catalogs.Add(new DirectoryCatalog(Path.Combine("BaseDirectory","PathToAssemblies"));

        _container = new CompositionContainer(catalog);

        try
        {
            //This line of code is what ties everything together.
            this._container.ComposeParts(this);
        }
        catch (CompositionException ex)
        {
            ....
        }
    }
}


I would create this class as start up, just keeping it tucked away somewhere. If you want to get really fancy, you can add a FileSystemWatcher on the directory that you specified so if you add a new file at runtime, it calls a function that automatically loads the new assembly and ties all of the Imports and Exports together.

That is pretty much it. This allows you to create an easily extendable program, if you need to add a DocumentCProcessor you would create the class in its own assembly and add the file to the directory that you specified in the MEFManager. The next time the program starts, the MEFManager will find the files and automatically import them to where they need to be.

Surprisingly, the MSDN documentation on MEF is fairly good and can be found here.