Composing from a Factory

Aug 6, 2010 at 7:35 PM
Edited Aug 6, 2010 at 7:50 PM

Hi,

Not sure how to approach this one. Maybe my overall design is incorrect or someone can point me in the right direction.

 

Lets say I have an IMessageService and an Implementation of this called ConsoleMessageService.

IMessageService has a definition for an ILogger Logger{get;set;} and a function of void SendMessage(string message);

Lets assume I also have a Logger class that implements ILogger and Logger is Exported as a typeof(ILogger).

 

Each ConsoleMessageService depends on custom Settings so they are passed in at runtime to the ctor by my MessageServiceFactory.

 


public class ConsoleMessageService : IMessageService
{
public ConsoleMessageService(MessageSettings config)
{
//Configure service using custom config
}

[Import]
public ILogger Logger{get; set;}

public void SendMessage(string message)
{
//Send message
}
}

 

Settings get read from a Configuration class  (wrapper around an xml file for example) which contains a collection of settings information for each MessageService I want to create an instance of.

Lets say each item in the collection is an instance of MessageSettings.

 So based on this Collection of MessageSettings I want to create an instance of my MessageService using one of these MessageSettings objects.

To do this I use a factory:

 

    [Export]
public class MessageServiceFactory
{
public IMessageService CreateService(MessageSettings settings)
{
return new ConsoleMessageService(settings);
}
}

 

 

 

To bring it all together I have a MessageServiceController which I want to use to manage a collection of all these IMessageService instances

The MessageController takes my Configuration type as a constructor parameter which gets resolved using [ImportingConstructor].

It also has an imported property for the ServiceFactory and implements IPartImportsSatisfiedNotification to know when it should begin processing the configuration settings.

Within this implemented interface I use the Factory to Create instances of my MessageService passing in the MessageSettings for each one which is retreived from my Configuration class

 

[Export(typeof(MessageServiceController))]
public class MessageServiceController : IPartImportsSatisfiedNotification
{
private IConfiguration _config = null;

[ImportingConstructor]
public ServiceController(IConfiguration configuration)
{
_config = configuration;
}

[Import]
public MessageServiceFactory ServiceFactory { get; set; }

public Collection<IMessageService> _messageServices = null;
public Collection<IMessageService> MessageServices
{
get
{
if (_messageServices == null) _messageServices = new Collection<IMessageService>();
return _messageServices;
}
}

private void CreateMessageServices()
{
foreach(MessageSettings ms in _config.MessageSettingsCollection)
{
this.MessageServices.Add(this.ServiceFactory.CreateService(ms));
}
}

#region IPartImportsSatisfiedNotification Members

public void OnImportsSatisfied()
{
CreateMessageServices();
}

#endregion
}

 

 

 Finally, my Program.cs imports MessageServiceController and uses its instance

 

    class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}

[Import]
public MessageServiceController SeviceController { get; set; }

public void Run()
{
Compose();

foreach (IMessageService ms in this.SeviceController.MessageServices)
{
ms.SendMessage("Test");
}
Console.ReadLine();
}

private CompositionContainer _container;
public void Compose()
{
var catalog = new DirectoryCatalog(@".\");
_container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();

batch.AddPart(this);

try
{
_container.Compose(batch);
}
catch (CompositionException ex)
{
Console.WriteLine(String.Join(Environment.NewLine, ex.Errors.Select(err => err.Exception.Message).ToArray()));
}
}

 

 

My Problem is that the MessageServices I created at runtime by 'newing' them up via the Factory are not actually composed so If I make a call to the ILogger property in MessageService it will throw an object reference error. What I would like to know is how can I compose the MessageServices in the factory?

Should I be creating separate instances of the container and reloading catalogs to compose it or is there a better approach to composing objects that are created through a factory that also depend on other MEF parts?

I've searched for some MEF factory examples online but the objects they created via the factory never contained other MEF imports/exports.