How to Satisfy Imports in a seperate class?

Mar 30, 2011 at 3:54 AM

I am fairly new to DDD and IoC but have a general understanding of how this should work. Let's say I have a class like:

namespace FAQ
{
    public class AskAQuestion
    {
        [Import(typeof(IQuestion))]
        public IQuestion question { get; set; }

        [Import(typeof(IAnswer))]
        public IAnswer answer { get; set; } 

        public AskAQuestion()
        {
        }

        public void Ask()
        {
            Console.WriteLine("Question is: " + question.Question);            
            Console.WriteLine("Answer is: " +  answer.Answer);
        }


    }
}
And in my consuming console application I have a few classes that implement IAnswer and IQuestion:
namespace FAQ.MEF.CLI
{
    [Export(typeof(IAnswer))]
    public class MyAnswer : IAnswer
    {
        #region IAnswer Members
        public string Answer
        {
            get
            {
                return "This is my answer";
            }
            set
            {
                throw new NotImplementedException();
            }
        }
        #endregion
    }
    
    [Export(typeof(IQuestion))]
    public class MyQuestion : IQuestion
    {
        #region IQuestion Members
        public string Question
        {
            get
            {
                return "Here is my question";
            }
            set
            {
                throw new NotImplementedException();
            }
        }
        #endregion
    }
}

In the Main() routine of my console app I have the usual import:
namespace FAQ.MEF.CLI
{
class Program
{    
        static void Main(string[] args)
        {
            Program p = new Program();

            AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            CompositionContainer container = new CompositionContainer(catalog);
            container.ComposeParts(p, new AskAQuestion());
  
            AskAQuestion aaq = container.GetExportedValue<AskAQuestion>();

            aaq.Ask();

            Console.ReadLine();
        }
}
}
However, when I run the application I get an ImportCardinalityMismatchException. I am obviously missing a step here. I am wondering if I cannot satisfy Imports in my Core assembly? 
I just want to make sure my Core Library remains open to IoC in an easy manner.
BTW - This works with Ninject but I am trying to stick with MEF when possible.
Thanks!
Developer
Apr 9, 2011 at 4:59 PM
You are getting the ImportCardinalityMismatchException for this line:
AskAQuestion aaq = container.GetExportedValue<AskAQuestion>();

The reason this does not work is AskAQuestion is not exported.

There are two solutions to solving the problem you want:
1) If you really want to call ComposeParts on AskSQuestion then just do that and the imports on AskSQuestion will be satisfied.
2) Add an Export attribute on AskSQuestion and just skip calling ComposeParts on it and just call GetExportedValue<AskAQuestion> to get it and it's imports satisfied.

Perhaps the thing that is confusing you right now is calling ComposeParts, what that does is simply add a particular object instance to the container and expose any exports it has as well as satisfy any imports it has.
Apr 9, 2011 at 6:13 PM

Thanks. That helps. I have something working now though I am not sure if this is the 100% correct way of doing it. I was still getting the same error as above when only adding an [Export] attribute to the AskAQuestion class. However, when I did the following it worked.

Added the Export attribute to my core class AskAQuestion:

[Export]
    public class AskAQuestion
    {
        [Import(typeof(IQuestion))]
        public IQuestion question { get; set; }

        [Import(typeof(IAnswer))]
        public IAnswer answer { get; set; } 

        public AskAQuestion()
        {
        }

        public void Ask()
        {
            Console.WriteLine("Question is: " + question.Question);            
            Console.WriteLine("Answer is: " +  answer.Answer);
        }


    }
 
Changed the ComposeParts on my consuming program to only compose for AskAQuestion:
    class Program
    {    
        static void Main(string[] args)
        {
            Program p = new Program();

            AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            CompositionContainer container = new CompositionContainer(catalog);
            container.ComposeParts(new AskAQuestion());

            AskAQuestion aaq = container.GetExportedValue<AskAQuestion>();

            aaq.Ask();

            Console.ReadLine();
        }
    }
So now, anywhere in my program, assuming I have access to the "container" I just created, I should be able to get the singleton AskAQuestion class with it's imports satisfied by the implementations I created. If there is a more simplistic way of doing this please let me know.
Thanks again.
Developer
Apr 11, 2011 at 6:42 AM

I would simply remove the container.ComposeParts(new AskAQuestion()) because it is not needed.

With the Export attribute on AskAQuestion anyone can pull it out of the container via container.GetExportedValue<AskAQuestion>() or simply import it themselfs, in which case they don't need to have direct access to the container.