Nested Assemblies with MEF

Dec 18, 2008 at 1:29 PM
Edited Dec 19, 2008 at 8:35 AM
Hi,

I am trying to create a hierarchy of classes that are loaded dynamically with MEF.  The classes are all located in seperate assemblies. 

The problem I am getting is that when I compose the parent assembly (Player) it complains it can't find the the childs (Playlist) Property class (Logger). 
At this point none of the code has any reference to the Logger. 
It is the playlist compose that needs to find the Logger. 
If I add the Logger to the Players catalog, even though it is not directly required the code runs but 2 Loggers are instantiated.

I am not sure if I am going about this all wrong, or my code is wrong.
Please help as I really want this thechnology to work as it is very cool.
Many Thanks

Frazer

Message
The composition produced 1 composition issues. The first few issues of severity level 'error' are provided below. Review the Issues collection for more detailed information.
CM:CardinalityMismatch : Error : No exports were found that match the constraint '(exportDefinition.ContractName = "MungleFish.Presenter.Model.ILogger")'.

Stack Trace
   at System.ComponentModel.Composition.CompositionResult.ThrowOnErrors()
   at System.ComponentModel.Composition.MutableExportProvider.Compose()
   at System.ComponentModel.Composition.CompositionContainer.Compose()
   at MungleFish.Presenter.Player.Compose() in H:\SSCode\C#\MF\MEF\MungleFish.Presenter.Player\Window1.xaml.cs:line 137
   at MungleFish.Presenter.Player..ctor() in H:\SSCode\C#\MF\MEF\MungleFish.Presenter.Player\Window1.xaml.cs:line 20

The sample code is all in a single file which does not produce the error.  In order to recreate you will need to make the 4 seperate assemblies.

namespace

 

Test {

 

 

 

using System;

 

 

 

using System.ComponentModel.Composition;

 

 

 

using System.Reflection;

 

 

 

//Main Executable

 

 

 

class Program {

 

 

 

static void Main(string[] args) {

 

 

 

Player p = new Player();

 

}

}

 

 

public class Player : IPlayer {

 

 

 

public Player() {

 

 

 

this.Compose();

 

}

[

 

Import]

 

 

 

public IPlaylist Playlist { get; set; }

 

 

 

public void Compose() {

 

 

 

Assembly executing = Assembly.GetExecutingAssembly();

 

 

 

AttributedAssemblyPartCatalog cat = new AttributedAssemblyPartCatalog(executing);

 

 

 

var catalog = new AggregatingComposablePartCatalog();

 

catalog.Catalogs.Add(

 

new AttributedAssemblyPartCatalog("Playlist.dll"));

 

 

 

var container = new CompositionContainer(catalog);

 

container.AddPart(

 

this);

 

container.Compose();

}

}

 

 

 

//Logger Assembly

 

 

[

Export(typeof(ILogger))]

 

 

 

public class Logger : ILogger {

 

 

 

public void Log(String message) {

 

System.Diagnostics.

 

Debug.WriteLine(message);

 

}

}

 

 

 

//Playlist Assembly

 

 

[

Export(typeof(IPlaylist))]

 

 

 

public class Playlist : IPlaylist {

 

 

 

public Playlist() {

 

 

 

this.Compose();

 

 

 

this.Logger.Log("Playlist Initialized");

 

}

[

 

Import]

 

 

 

public ILogger Logger { get; set; }

 

 

 

public void Compose() {

 

 

 

Assembly executing = Assembly.GetExecutingAssembly();

 

 

 

AttributedAssemblyPartCatalog cat = new AttributedAssemblyPartCatalog(executing);

 

 

 

var catalog = new AggregatingComposablePartCatalog();

 

catalog.Catalogs.Add(

 

new AttributedAssemblyPartCatalog("Logger.dll"));

 

 

 

var container = new CompositionContainer(catalog);

 

container.AddPart(

 

this);

 

container.Compose();

}

}

 

 

 

//Model Assembly

 

 

 

public interface IPlayer {

 

 

 

void Compose();

 

 

 

IPlaylist Playlist { get; set; }

 

}

 

 

public interface IPlaylist {

 

 

 

void Compose();

 

 

 

ILogger Logger { get; set; }

 

}

 

 

public interface ILogger {

 

 

 

void Log(String message);

 

}

}

 



Dec 18, 2008 at 3:52 PM
do all assemblies reside in the same folder?
some posts back there was some talk about using the directorypartCatalog. Would that work instead of AttributedAssemblyPartCatalog ?

there is also this problem someone said about the order in which the assemblies have to be loaded. I do not know if this is resolved.

Not to oversimplify the MEF effort. But I think it works best when all classes are in one single dll. or the dlls have no dependancy between them.

Dec 18, 2008 at 4:40 PM
Hi ganesh,

The assemblies are all in the same folder.  Do you think I should seperate them?
I didn't  use th DirectoryPartCatalog because all the dll's were in the same folder.

I have got MEF working well when all classes are in a single dll but this only provides me with extensibility at design time.
Ideally I need to just make use of new dll's at runtime (like in the tetris example with the extra shapes).
However this does not seem to work when the assemblies are nested.

Thanks

Frazer
Dec 18, 2008 at 6:34 PM
Frazer, there was some discussion about the order of loading dlls here. This might not be what you are looking for but the reasoning of directorypart catalog is on that link.

HTH
Ganesh
Dec 18, 2008 at 7:51 PM
Hi Ganesh,
I have read the thread regarding the ordering.  I don't think this is going to help. 
The problem is not the order in which they are loading rather the fact I would not expect and do not want any of the child assemblies to be loaded until a class that imports them runs the compose method.

Also I have now recreated the same error using the DirectoryPartCatalog as well.

Thanks

Frazer
Dec 19, 2008 at 8:59 AM
Hi All,

I have solved it...
I am not sure if this is the right way to do it but the code runs.
Rather than using the Import attribute and Compose I am calling GetExportedObjectOrDefault<T>
see sample Player class below...

Many Thanks

Frazer

 

public class Player : IPlayer {

 

 

private readonly IPlaylist _playlist = null;

 

 

public Player() {

 

 

var catalog = new AggregatingComposablePartCatalog();

 

catalog.Catalogs.Add(

new DirectoryPartCatalog(Environment.CurrentDirectory));

 

 

var container = new CompositionContainer(catalog);

 

container.AddPart(

this);

 

_playlist = container.GetExportedObjectOrDefault<

IPlaylist>();

 

}

 

public IPlaylist Playlist {

 

 

get {

 

 

return _playlist;

 

}

}

}