tags:

views:

540

answers:

2

Hi,

I try for some time to get things done using MEF but now, I run into a problem i need help.

Description: I have 2 DLL and one EXE file. ClassLibrary1 (LoggerImpl.cs, SomeClass.cs) ClassLibrary2 (ILogger.cs) WindowsApplicationForms1 (WindowsApplicaitonForms1.cs, Program.cs)

I need any help or direction why this doesn't work ?

// ClassLibrary1.dll
//SomeClass.cs
 public class SomeClass
    {
        [Import("Logging", typeof(ILogger))]
        public ILogger Log { get; set; } <-- ALWAYS NULL ???

        public void Print()
        {
            Log.Print();
        }

    }

// ClassLibrary1.dll
// LoggerImpl.cs
namespace ClassLibrary1
{
    [Export("Logging", typeof (ILogger))]
    public class LoggerImpl : ILogger
    {
        public void Print()
        {
            Console.WriteLine("print called");
        }
    }
}

// ClassLibrary2.dll
// ILogger.cs
namespace LogNamespace
{
    public interface ILogger
    {
        void Print();
    }
}

// WindowsFormsApplication1.exe
// WindowsFormsApplication1.cs
namespace WindowsFormsApplication1
{
    [Export("Form1",typeof(Form1))]
    public partial class Form1 : Form
    {

        [Import("Logging", typeof(ILogger))]
        public ILogger Log { set; get; }

        private CompositionContainer _container;

        public Form1()
        {
            InitializeComponent();
            Compose();
            Log.Print();

            SomeClass c = new SomeClass();
            c.Print();
        }

        private void Compose()
        {
            var catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new DirectoryCatalog("."));
            catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
            _container = new CompositionContainer(catalog);

            try
            {
                _container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                MessageBox.Show(compositionException.ToString());
            }
        }
    }
}
A: 

You need a statement like similar to the following to involve the SomeClass in the composition process

// ClassLibrary1.dll
//SomeClass.cs
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Windows.Forms;
using LogNamespace;

public class SomeClass
{
    [Import("Logging", typeof(ILogger))]
    public ILogger Log { get; set; } //<-- ALWAYS NULL ???

    public SomeClass()
    {
        var catalog = new AggregateCatalog();
        CompositionContainer _container;

        // catalog.Catalogs.Add(new DirectoryCatalog("."));
        catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
        _container = new CompositionContainer(catalog);

        _container.ComposeParts(this);
    }

    public void Print()
    {
        Log.Print();
    }

}

// ClassLibrary1.dll
// LoggerImpl.cs
namespace ClassLibrary1
{
    [Export("Logging", typeof(ILogger))]
    public class LoggerImpl : ILogger
    {
        public void Print()
        {
            Console.WriteLine("print called");
        }
    }
}

// ClassLibrary2.dll
// ILogger.cs
namespace LogNamespace
{
    public interface ILogger
    {
        void Print();
    }
}

// WindowsFormsApplication1.exe
// WindowsFormsApplication1.cs
namespace WindowsFormsApplication1
{
    [Export("Form1", typeof(Form1))]
    public partial class Form1 : Form
    {

        [Import("Logging", typeof(ILogger))]
        public ILogger Log { set; get; }

        private CompositionContainer _container;

        public Form1()
        {
            InitializeComponent();
            Compose();
            Log.Print();

            SomeClass c = new SomeClass();
            c.Print();
        }

        private void Compose()
        {
            var catalog = new AggregateCatalog();

            // catalog.Catalogs.Add(new DirectoryCatalog("."));
            catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
            _container = new CompositionContainer(catalog);

            try
            {
                _container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                MessageBox.Show(compositionException.ToString());
            }
        }
    }
}
simon_bellis
Thanks for response.So if I add another DLL with parts, I also must change client code (read Compose method) to include those parts?I was thinking that if I create another Part and put into directory with app, application can use that parts if app itself contains appropriate Import (like, Meni, Logger, Toolbar etc.).
ITGoran
In this code you are creating two separate containers, which is probably not what you want. With two containers, you would get two separate instances of the logger in your Form1 class and your SomeClass.
Daniel Plaisted
+4  A: 

If you create a new instance of a class yourself (new SomeClass()), the container won't know anything about it and won't compose it.

For a part to be composed by MEF, it needs to be created by MEF, or passed explicitly to the container. You can manually tell MEF to satisfy the SomeClass object's imports in the same way you told it to satisfy the form's imports:

SomeClass c = new SomeClass();
_container.SatisfyImports(c);
c.Print();

However, you need direct access to the container to do this, so it doesn't work as well outside of your Form1 class. In general, a better way to do it would be to export SomeClass, and create an import in your Form1 class for SomeClass:

[Export]
public class SomeClass
{
    [Import("Logging", typeof(ILogger))]
    public ILogger Log { get; set; }

    // etc.
}

public partial class Form1 : Form
{
    [Import("Logging", typeof(ILogger))]
    public ILogger Log { set; get; }

    [Import]
    SomeClass _someClass { get; set; }

    // etc.
}
Daniel Plaisted
+1. The primary issue IS the SomeClass not having a composition container itself. If SomeClass was turned into an interface itself, an IEnumerable<SomeClass> (for example) could be used along with the ImportMany attribute so that it turns into a very simple way of adding parts by just dropping assemblies into the directory w/o additional code.
JamesEggers