Newbie Question.

Nov 4, 2009 at 1:54 PM

Hello,

I am starting with MEF. I am trying to write a simple application just for the sake of exploring the MEF functionalities. 

I have a solution with 3 projects, a client, another project where I have the contract (in an interface) and the third project contains the part (and implements the interface mentioned earlier)

The following client code 

 

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Reflection;
using TestInterface;

namespace Client
{
    public class Client
    {
        [Import]
        public Lazy<IShape> Shape { get; set; }

        public static void Main(string[] args)
        {
            var client = new Client();
            client.Compose();
            client.Run();
        }

        public void Run()
        {            
            if(this.Shape != null)
            {
                this.Shape.Value.Draw();
            }
            Console.Read();
        }

        private void Compose()
        {
            try
            {
                var catalog =
                    new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);

                var agcatalogue =
                    new AggregateCatalog(new ComposablePartCatalog[]
                                             {
                                                 catalog,
                                                 new AssemblyCatalog(Assembly.GetExecutingAssembly())
                                             });

                var container = new CompositionContainer(agcatalogue);                

                var batch = new CompositionBatch();

                batch.AddPart(this);

                container.Compose(batch);
            }
            catch (FileNotFoundException fnfex)
            {
                Console.WriteLine(fnfex.Message);
            }
            catch (CompositionException cex)
            {
                Console.WriteLine(cex.Message);
            }
        }
    }
}

 

 

is throwing a CompositionException exception with error message as displayed below. My understanding is that the exported part (an implementation of IShape) will be discovered. Wondering why it does not work. Hints and suggestions much apreciated. Thanks.

<exception>

The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for moredetailed information.

 

1) No valid exports were found that match the constraint '((exportDefinition.ContractName = "TestInterface.IShape") && (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && "TestInterface.IShape".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.

 

Resulting in: Cannot set import 'Client.Client.Shape (ContractName="TestInterface.IShape")' on part 'Client.Client'.Element: Client.Client.Shape (ContractName="TestInterface.IShape") --> Client.Client

 </exception>

 

 

    public class Client
    {
        [Import]
        public Lazy<IShape> Shape { get; set; }
        public static void Main(string[] args)
        {
            var client = new Client();
            client.Compose();
            client.Run();
        }
        public void Run()
        {            
            if(this.Shape != null)
            {
                this.Shape.Value.Draw();
            }
            Console.Read();
        }
        private void Compose()
        {
            try
            {
                var catalog =
                    new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
                var agcatalogue =
                    new AggregateCatalog(new ComposablePartCatalog[]
                                             {
                                                 catalog,
                                                 new AssemblyCatalog(Assembly.GetExecutingAssembly())
                                             });
                var container = new CompositionContainer(agcatalogue);                
                var batch = new CompositionBatch();
                batch.AddPart(this);
                container.Compose(batch);
            }
            catch (FileNotFoundException fnfex)
            {
                Console.WriteLine(fnfex.Message);
            }
            catch (CompositionException cex)
            {
                Console.WriteLine(cex.Message);
            }
        }
    }

 

 

Nov 4, 2009 at 5:27 PM
you should use ComposeParts instead of Comose
see the following sample:
 
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace ConsoleApplication4MEF
{
    class Program
    {
        static void Main(string[] args)
        {
            var foo = new Foo();
            var container = new CompositionContainer();
            container.ComposeParts(foo);
            var f = container.GetExportedValue<IFoo>();
        }
    }

    [Export( typeof(IFoo))]
    public class Foo : IFoo
    { 
    }

    public interface IFoo
    { 
    }
}

 

Nov 4, 2009 at 5:57 PM

Thanks for this, but do you need to have all classes in the same project? I have 3 different projects in the same assembly (one for the contract, one for the part and the client which I have copied in my initial post). My understanding is that MEF will find the lib where the part is defined and use it to call the Draw()-Method. With your suggestion, I managed to get rid of the exception, however, my shape object is null and the Draw() method does not get called.

Thanks again.

Developer
Nov 4, 2009 at 6:42 PM

What you have should work, assuming that the assembly that contains the export of IShape is actually in the directory you are point for the directory catalog. Did you verify that the assembly and export is being discovered by the directory catalog?

Nov 5, 2009 at 10:46 AM
Edited Nov 5, 2009 at 11:06 AM

Thanks wes, I will check the directories.

Sorry if this question is a bit trivial, but how can I verify that the assembly and export arebeing discovered?

ta

Nov 5, 2009 at 11:18 AM
Edited Nov 5, 2009 at 11:20 AM
betol wrote:

Thanks wes, I will check the directories.

Sorry if this question is a bit trivial, but how can I verify that the assembly and export arebeing discovered?

ta

I was indeed missing the directory where the part library was residing. the problem has now been rectified. In my VS2008 debug window, I can also see the notification that the dll has been loaded.

Now I am trying to verify the assembly has been discovered, using the GetExportedValueOrDefault(...) as described in the guide. The result is indeed correct, however, my Shape attribute is still null so that the Draw(...) method is not being called.

Below the amended code.

ta

 

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Reflection;
using TestInterface;
namespace Client { public class Client { [Import] public IShape Shape { get; set; } public static void Main(string[] args) { var client = new Client(); client.Compose(); client.Run(); } public void Run() { if(this.Shape != null) { this.Shape.Draw(); } else { Console.WriteLine("Shape is null!!!"); } Console.Read(); } private void Compose() { try { var catalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory); var catalog2 = new DirectoryCatalog(@"...<<truncated>>...\Debug"); var agcatalogue = new AggregateCatalog(new ComposablePartCatalog[] { catalog, catalog2, new AssemblyCatalog(Assembly.GetExecutingAssembly()) }); var container = new CompositionContainer(agcatalogue); var batch = new CompositionBatch(); batch.AddPart(this); container.ComposeParts(batch); var part = container.GetExportedValueOrDefault<IShape>(); Console.WriteLine(">> " + part); } catch (FileNotFoundException fnfex) { Console.WriteLine(fnfex.Message); } catch (CompositionException cex) { Console.WriteLine(cex.Message); } } } }

 

 

 

                var part = container.GetExportedValueOrDefault<IShape>();
                Console.WriteLine(">> " + part);

 

Developer
Nov 5, 2009 at 5:21 PM

OK. container.ComposeParts is an extension method that allows you to eliminate the CompositionBatch you should call container.ComposeParts(this) instead of on the batch object.

Nov 5, 2009 at 5:36 PM

Works! Thanks.