4
votes

I am a little confused by MEF, I thought I had begun to understand it but it appears I am not quite there.

So, I have a list of test steps in XML that I want to read in. The idea is that the main application doesn't know anything about the types at run time.

<TestSteps>
    <TestRunner xsi:type="SimulateDc" Measurement="54"/>
    <TestRunner xsi:type="MeasureDc" Output="1"/>
</TestSteps>

So I have a base type with a static "results" class that allows me to save information to pass between steps (The Output attribute in the XML above). The test handlers here are exported by MEF, I read them in at runtime and then get the Type to pass into the XML serializer to create a list of handlers. This all works and I get a list of test runners. I have a DataTemplate export for each type here so when I use a Content Control it knows how to draw itself. All all seems fine, however I think I have gone wrong in my thought process.

One issue is that I now want to tie the imported handlers to some hardware. the hardware handling routines are intended to be handled by yet more MEF imports

So with an interface like this:

public interface IMeasureHW
{
    double Measure();
}

Then using this:

[Export("MeasureDc", typeof(IMeasureHW))]
public class MeasureDcHW : IMeasureHW
{
    public double Measure()
    {
        return 54.0;
    }
}

Then in one of my test handlers I have done this:

[Import("MeasureDc", typeof(IMeasureHW))]
IMeasureHW hardware { get; set; }

My importing is carried out like this:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    var catalog = new AggregateCatalog();
    catalog.Catalogs.Add(new DirectoryCatalog("."));
    catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));

    _container = new CompositionContainer(catalog);

    _container.ComposeParts(this);

    MainWindow.Show();
}

However, I am guessing the XML serialization above and using the Type information as I do would definitely mean the import would be null so implying my thought patterns for the design are in error.

I did manage to get it to work by exporting the CompositionContainer and then after loading the XML I am able to do this:

foreach (TestRunnerBase t in testSteps)
{
    _container.SatisfyImportsOnce(t);
}

But that feels a bit wrong to me, as the initial list of imported test steps isn't being used for anything apart from getting the type. So I was thinking that I should be exporting the data as MEF parts and then independently exporting the handlers, then somehow when I read in my list of data from the XML I ask for a handler from the list? If that makes sense?

I couldn't work out how you would tie them together in this way as the MEF composition is all handled in my App.xaml.cs and the test steps are in a view model elsewhere. I was thinking something along the lines of using metadata to tie data to a handler, and then finding the corresponding handler in the imported list. Perhaps I should carry out an initial parse to build a dictionary to speed up lookups?

Is this more the way it should be done? Any help appreciated, I am already quite light in the hair department so I can't afford to lose more

1
Do you create test handler via MEF container? Could you post the code, which performs container and catalog creation?Dennis
I made some errors as it turns out, the initial import wasn't null. So I have updated the question accordingly as it is actually different to what I thought.Firedragon
Did you have a look at MEF's Export Metadata? mef.codeplex.com/wikipage?title=Exports%20and%20MetadataPanos Rontogiannis

1 Answers

0
votes

Please correct me if I'm wrong - It seems as though you could achieve your goal by chaining imports: innermost being your TestRunner collection, then the hardware classes, then finally the content control.

In the example below these are Class2 : MySubInterface, Class1 : MyInterface, and Program respectively:

/////inner

using MyHostingNamespace;
namespace ClassLibrary1
{


    [Export("class2", typeof(MySubInterface))]
    class Class2 : MySubInterface
    {
        public string MyProperty { get; set; }

        public Class2()
        {

            MyProperty = "Class2";
        }
    }
} 

////middle

using MyHostingNamespace;
namespace ClassLibrary1
{


    [Export("class1", typeof(MyInterface))]
    public class Class1 : MyInterface
    {
        [Import("class2", AllowDefault=true)]
        MySubInterface myClass2;

        public string MyProperty {get;set;}

        public Class1()
        {

            AggregateCatalog catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            CompositionContainer _container = new CompositionContainer(catalog);
            _container.ComposeParts(this);

            MyProperty = myClass2.MyProperty;
        }
    }
}

////outer

namespace MyHostingNamespace
{
    class Program
    {
        [Import("class1")]
        public MyInterface class1;

        public Program()
        {
            AggregateCatalog catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            catalog.Catalogs.Add(new DirectoryCatalog("."));
            CompositionContainer _container = new CompositionContainer(catalog);

            _container.ComposeParts(this);

        }

        static void Main(string[] args)
        {

            Program p = new Program();

            Console.WriteLine(p.class1.MyProperty);

        }

    }



    public interface MyInterface
    {
        string MyProperty { get; set; }
    }

    public interface MySubInterface
    {
        string MyProperty { get; set; }
    }
}