2

When attempting to load an instantiated export with GetExports() (using a LINQ query described below), the method returns null. I notice that when I call GetExports without the LINQ query, the return value is Count: 0. This would indicate to me that MEF is failing to find any exports that have been composed in the container. I can see the ExportDefinition, however, when looking at Container.Catalog.Parts.ExportDefinitions. Any ideas on where I am going wrong? Everything up until the query seems to be working fine.

I have the following contract and metadata view declared and implemented:

public interface IMap
{
    void Init();
    int ParseData();
}

public interface IMapMetadata
{
    string MapName { get; }
    string DocumentType { get; }
}

[Export(typeof(IMap))]
[ExportMetadata("MapName", "Map")]
public class Map
{
    public Map()
    {
    }
}

I am using the following code to load a directory that contains DLLs that satisfy this contract with:

    public void LoadByDirectory(string zPath)
    {
        try
        {
            _catalog.Catalogs.Add(new DirectoryCatalog(zPath));
        }
        catch (Exception e)
        {
            String zErrMess = e.Message;
        }

    }

Using a LINQ query to get an export:

public IMap GetMapInstance(string zMapName)
{
        IMap ndeMap;

        _container = new CompositionContainer(_catalog);
        _container.ComposeParts(this);

        try
        {
            ndeMap = _container.GetExports<IMap, IMapMetadata>()
                            .Where(p => p.Metadata.MapName.Equals(zMapName))
                            .Select(p => p.Value)
                            .FirstOrDefault();
        }
        catch (Exception ex) 
        {
            throw new Exception("Failed to load map " + zMapName + ": " + ex.Message, ex);
        }

        return ndeMap;
    }

Calling the above method like this:

IMap map = mapFactory.GetMapInstance("Map");

returns null.

UPDATED

In addition to the answer below, I was forgetting to declare the interface on the map class, this resolves the issue (note I removed the DocumentType property):

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MapExportAttribute : ExportAttribute, IMapMetadata
{
    public MapExportAttribute()
        : base(typeof(IMap))
    {
    }

    public string MapName { get; set; }
}

[MapExport(MapName="Map")]
public class Map : IMap
{
    public Map()
    {
    }

    public void  Init()
    {
        throw new NotImplementedException();
    }

    public int  ParseData()
    {
        throw new NotImplementedException();
    }
}
cdlm
  • 565
  • 9
  • 20
  • 1
    When debugging, is Map registered in the container? Have you used the attribute's parameters in the right order? BTW: You should change the end of the linq query to not instantiate maps you don't use then: instead of .Select(p => p.Value).FirstOrDefault(), use: .FirstOrDefault().Value. That doesn't solve the problem though... Does this help: http://stackoverflow.com/questions/10988447/mef-getexportst-tmetadataview-returning-nothing-with-allowmultiple-true ? – Marc Mar 12 '13 at 16:43
  • Thanks Marc, I resolved the issue but will definitely be using your suggestion for the LINQ query, nice catch ! – cdlm Mar 12 '13 at 18:19

1 Answers1

1

It looks like you're missing the DocumentType meta-data on your export:

[Export(typeof(IMap))]
[ExportMetadata("MapName", "Map")]
[ExportMetadata("DocumentType", "???")]
public class Map
{
}

The simplest way to ensure you specify the correct meta-data is a custom export attribute:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class MapExportAttribute : ExportAttribute, IMapMetadata
{
   public MapExportAttribute() : base(typeof(IMap))
   {
   }

   public string MapName { get; set; }
   public string DocumentType { get; set; }
}

[MapExport(MapName = "Map")]
public class Map
{
}
Richard Deeming
  • 29,830
  • 10
  • 79
  • 151