MEF load types that implement interface and match attributes
Asked Answered
P

1

7

Lets say i have an interface IFileLoader and the attribute FileTypeAttribute

And I implement IFileLoader in JPEGLoader with [FileType(".jpg")]

Can i use MEF to load the class (JPEGLoader) that implements IFileLoader and matches .jpg as file extension so I can implement the following method:

public void IFileLoader GetLoader(string filename);

Can this be done with MEF, or should I stick to this:

var allTypes = assemblies.SelectMany(a => a.GetTypes());
var classes = allTypes.Where(t => t.IsClass && ! t.IsAbstract);
var fileLoaders = classes.where(t => typeof(IFileLoader).IsAssignableFrom(t));
var forType = fileLoaders.Where(t => t.GetAtributeValue<FileTypeAttribute,string>(t => t.FileType, string.Empty) == fileType);
var loaderInstances = fileLoaders.Select(t => Activator.CreateInstance(t) as IFileLoader);

Or the above turned into ILookup at least, or maybe something else that I haven't considered?

I'd like to be able to implement the IFileLoader in different assemblies of the project or even in plugin assemblies.

Phosphatize answered 13/5, 2014 at 11:12 Comment(0)
I
2

It is possible to get the file loader you need by using MEFand attachnig Metadata to the exported parts.

Create metadata interface and attribute.

public interface IFileTypeMetadata
{
    string FileExtension { get; }
} 

The interface can contain as many properties you need. For this particular problem, it contain only one property FileExtension. It is important that the property has only getter. MEF is designed not to allow changing of metadata at runtime. Then create the attribute that holds the metadata:

public class FileTypeAttribute : Attribute, IFileTypeMetadata    
{        
    public string FileExtension { get; set; }
}

The FileTypeAttribute class implements IFileTypeMetadata interface, and you can add a setter to the FileExtension property. It is needed for implementing the FileLoader classes, and MEF will not complain because we will access the metadata via the IFileTypeMetadata interface which contain properties that have only getters

Create the interface that file loader classes implement and the file loader classes.

public interface IFileLoader
{
    string LoadFile();
}

The interface contains only one method for the sake of simplicity. And here is example of two different dummy file loader classes:

[Export(typeof(IFileLoader))]
[FileType(FileExtension = ".jpg")]
public class JpgFileLoader : IFileLoader
{
    public string LoadFile()
    {
        return "JPG"; 
    }
}

[Export(typeof(IFileLoader))]
[FileType(FileExtension = ".png")]
public class PngFileLoader : IFileLoader
{
    public string LoadFile()
    {
        return "PNG";
    }
}

Both classes implement the IFileLoader interface and are exported as IFileLoader parts, but they have different metadata, via the FileType attribute.

Create the composition container

var catalog = new DirectoryCatalog("path to directory where dll's are located");
var compositionContainer = new CompositionContainer(catalog);
compositionContainer.ComposeParts();

Access exported parts

var fileLoaders = compositionContainer.GetExports<IFileLoader, IFileTypeMetadata>();

var jpgFileLoader = fileLoaders.FirstOrDefault(x => x.Metadata.FileExtension == ".jpg");

if (jpgFileLoader != null)
    Console.WriteLine(jpgFileLoader.Value.LoadFile()); //should print "JPG"

var pngFileLoader = fileLoaders.FirstOrDefault(x => x.Metadata.FileExtension == ".png");

if (pngFileLoader != null)
    Console.WriteLine(pngFileLoader.Value.LoadFile()); //should print "PNG"
Interval answered 13/5, 2014 at 20:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.