locked
Comparison of CompositionContainer with traditional IoC Containers

    Question

  • Hello there,

    Could you give us a summary of the responsibilities of the CompositionContainer? How does it compare with a 'traditional' IoC container (Unity, Windsor) ?

    More specifically on the features:
    - Will the CompositionContainer handle component creation in the future (or do we always have to pass an instance to it)?
    - Is generic specialization part of the features CompositionContainer will support ?

    Thanks,
    Steve

    Friday, June 06, 2008 1:10 PM

Answers

  • A CompositionContainer is very similar in nature to a 'traditional' IoC container. The API's on CompositionContainer focus on three general things:

    1) Addition and Removal of exports and components
    2) Export querying (i.e. GetBoundValue(s), GetImportInfo(s))
    3) Component composition (i.e. Bind)

    One usage would be to have someone add components to the container manually and compose them together.

    CompositionContainer container = new CompositionContainer(); 
    container.AddComponent<Foo>(new Foo()); 
    container.AddComponent<Bar>(new Bar()); 
    container.Bind();

    While that way works it is not the only way (perhaps not even the preferred way). The CompositionContainer has the notion of a Resolver which is used for finding exports that can fulfill imports during composition. One commonly used resolver is the catalog resolver.

    AssemblyComponentCatalog catalog = new AssemblyComponentCatalog(); 
    catalog.AddAssembly("<path to foo.dll>"); 
    CompositionContainer container = new CompositionContainer(catalog.Resolver);

    Our current catalog examines the metadata in assemblies (i.e. looks for Export attributes) to build up a registry of exports that the container can use to fulfill imports. Based on the information in the catalog objects can be created for you automatically so you don't have to manually add or register individual instances. For example, suppose you have the Foo class below in an assembly that was added to the catalog, since Foo is decorated with the Export attribute it will be discovered by the catalog’s resolver and thus also by the container.

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

    To retrieve the instance of Foo you would query the container by calling GetBoundValue and by doing so Foo will automatically be instantiated and bound for you.

    // Query the container for IFoo 
    IFoo foo = container.GetBoundValue<IFoo>();

    There has been a lot of concern and questions about whether or not we should use attributes as our programming model however we feel it makes for a nice declarative model. That said, keep in mind that just because attributes are the default programming model it does not mean it is the only possible programming model. We feel that there are enough extensibility points in the system to actually create any number of programming models, including the popular DI style XML configuration model. We are looking into either shipping or at least providing samples of other such programming models

    Is generic specialization part of the features CompositionContainer will support?

    By default we had not planned to support this but we will certainly look into it if people think it will be useful. Even if we don't support this out of the box there should not be anything stopping someone else from extending our system to add support for it.

    HTH,
    Wes

    Blog: http://weblogs.asp.net/whaggard
    Saturday, June 07, 2008 6:00 AM
  • Stiiifff said:

    The CompositionContainer is a lightweight IoC container integrated in the .Net Framework, with native support for a variety of components composition scenarii, and with built-in extensibility to allow you to easily augment the container functionality by plugging in new concerns and semantics.


    Yes, that's correct.

    However, one advantage of the CompositionContainer that typical IoC containers do not support, is:

    • Metadata. Which is the ability to apply arbitarary data to an 'export', and have the 'import' consume this metadata and/or filter exports based on it.
    • Duck typing. This is ability to convert from one type to another, even if they do not share a common base class or interface.

    Duck type allows the following scenario to work:

    Given the following interface which is exported as 'Duck':

    public interface IDuck  
    {  
          void Quack();  
    }  
     
    [Export("Duck"]  
    public IDuck Duck
    {  
        get;  

    The following import which imports 'Duck' as a different interface, succeeds, because IDuck and ILookLikeADuck structurally match.

    public interface ILookLikeADuck  
    {  
        void Quack();  
    }  
     
     
    [Import("Duck")]  
    public ILookLikeADuck Duck  
    {  
           get;  
           set;  

    Calling the instance of ILookLikeADuck.Quack() will underneath call the exported implementors IDuck.Quack().


    Managed Extensibility Framework Team | My Blog: http://davesbox.com
    Saturday, June 07, 2008 3:59 PM

All replies

  • A CompositionContainer is very similar in nature to a 'traditional' IoC container. The API's on CompositionContainer focus on three general things:

    1) Addition and Removal of exports and components
    2) Export querying (i.e. GetBoundValue(s), GetImportInfo(s))
    3) Component composition (i.e. Bind)

    One usage would be to have someone add components to the container manually and compose them together.

    CompositionContainer container = new CompositionContainer(); 
    container.AddComponent<Foo>(new Foo()); 
    container.AddComponent<Bar>(new Bar()); 
    container.Bind();

    While that way works it is not the only way (perhaps not even the preferred way). The CompositionContainer has the notion of a Resolver which is used for finding exports that can fulfill imports during composition. One commonly used resolver is the catalog resolver.

    AssemblyComponentCatalog catalog = new AssemblyComponentCatalog(); 
    catalog.AddAssembly("<path to foo.dll>"); 
    CompositionContainer container = new CompositionContainer(catalog.Resolver);

    Our current catalog examines the metadata in assemblies (i.e. looks for Export attributes) to build up a registry of exports that the container can use to fulfill imports. Based on the information in the catalog objects can be created for you automatically so you don't have to manually add or register individual instances. For example, suppose you have the Foo class below in an assembly that was added to the catalog, since Foo is decorated with the Export attribute it will be discovered by the catalog’s resolver and thus also by the container.

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

    To retrieve the instance of Foo you would query the container by calling GetBoundValue and by doing so Foo will automatically be instantiated and bound for you.

    // Query the container for IFoo 
    IFoo foo = container.GetBoundValue<IFoo>();

    There has been a lot of concern and questions about whether or not we should use attributes as our programming model however we feel it makes for a nice declarative model. That said, keep in mind that just because attributes are the default programming model it does not mean it is the only possible programming model. We feel that there are enough extensibility points in the system to actually create any number of programming models, including the popular DI style XML configuration model. We are looking into either shipping or at least providing samples of other such programming models

    Is generic specialization part of the features CompositionContainer will support?

    By default we had not planned to support this but we will certainly look into it if people think it will be useful. Even if we don't support this out of the box there should not be anything stopping someone else from extending our system to add support for it.

    HTH,
    Wes

    Blog: http://weblogs.asp.net/whaggard
    Saturday, June 07, 2008 6:00 AM
  • Great info, thanks Wes. :)

    Still got one more question though (for now ;P).

    When you say that "A CompositionContainer is very similar in nature to a 'traditional' IoC container", it makes wonder:

    What does a CompositionContainer does (or will do) that an IoC container doesn't?


    Stated another way, can I define the CompositionContainer like this:

    The CompositionContainer is a lightweight IoC container integrated in the .Net Framework, with native support for a variety of components composition scenarii, and with built-in extensibility to allow you to easily augment the container functionality by plugging in new concerns and semantics.

    Saturday, June 07, 2008 10:04 AM
  • Stiiifff said:

    The CompositionContainer is a lightweight IoC container integrated in the .Net Framework, with native support for a variety of components composition scenarii, and with built-in extensibility to allow you to easily augment the container functionality by plugging in new concerns and semantics.


    Yes, that's correct.

    However, one advantage of the CompositionContainer that typical IoC containers do not support, is:

    • Metadata. Which is the ability to apply arbitarary data to an 'export', and have the 'import' consume this metadata and/or filter exports based on it.
    • Duck typing. This is ability to convert from one type to another, even if they do not share a common base class or interface.

    Duck type allows the following scenario to work:

    Given the following interface which is exported as 'Duck':

    public interface IDuck  
    {  
          void Quack();  
    }  
     
    [Export("Duck"]  
    public IDuck Duck
    {  
        get;  

    The following import which imports 'Duck' as a different interface, succeeds, because IDuck and ILookLikeADuck structurally match.

    public interface ILookLikeADuck  
    {  
        void Quack();  
    }  
     
     
    [Import("Duck")]  
    public ILookLikeADuck Duck  
    {  
           get;  
           set;  

    Calling the instance of ILookLikeADuck.Quack() will underneath call the exported implementors IDuck.Quack().


    Managed Extensibility Framework Team | My Blog: http://davesbox.com
    Saturday, June 07, 2008 3:59 PM
  • Very interesting, thanks David.

    Saturday, June 07, 2008 10:54 PM
  • The major difference between the two is the use cases they are optimized for:

    The primary usage for most DI containers such as castle, structure map, etc is NOT about extensibility, rather it is for internal decoupling within the application components, separation of concerns and testability. For example I might have an OrderProcessing component that needs to send an email. I don't want the OrderProcessing component to be responsible for actually sending the message because I want to be able to run OrderProcessing unit-tests without sending. So I refactor the message sending functionality to an EmailSender service which implements IEmailSender. The OrderProcessing component than takes an injected IEmailSender during its construction. Now in my unit test, I can pass it a MockInstance that does nothing but keep track of the fact that it was called. In this case I control the source code of both the OrderProcessing component and the EmailSender.

    On the same token I can use the DI container for extensibility, but when I do I get limited support out of the box. So in my EmailSender case, I can change it to a Sender service / ISender and have different kinds of senders EmailSender, WebServiceSender, FTPSender, etc. How does my application find my Sender classes though? I need to regsiter it with the container somehow either via config or code. I can write a custom mechansim to discover very specific types for example loop through the bin folder and find all assemblies that contain an ISender. But that's me doing the custom work. The container is not designed for that. It's primary role is to offer up concrete types based on an interface. Let's say your app requires an ISender and none is available. How will the app know? With DI containers, it won't.

    MEF is built entirely for extensibility / composition. What we mean by extensibility, is that the application consumes parts that were authored by developers who do not have access to the source of the application. MEF uses discovery as its primary means for registration. Instead of you telling MEF what to register, it goes out and explores the world to see what parts / exports are available. MEF apps "light up" by dropping binaries into the bin folder that contain these exports. This makes MEF optimal for consuming application components that are more coarse grained. For example lets say I have an editor that supports custom viewers for different file types. With MEF, my app would simply declare an import that is of type List<IFileViewer>. If I stop and start my app and drop an XmlViewer into my bin folder, and I restart the app, it will suddenly know how to display Xml files. I can additionally drop in a CSViewer and add support for CSharp files.

    In addition to just finding out what is available, MEF exports carry additional metadata. I can use this metadata to query the container for the types of things it has available with a fine level of granularity. I can do this without ever manfacturing any instances (delay loaded). In my viewer case, I can ask the container to give me just the metadata for the IFileViewers. Then I can loop through this to populate the file types browser. Only when someone opens a specific file type will I then manufacture the actual instance. Having the explicit attribute based approach means we can do static analysis of the application. We can know whether or not the things it needs are available

    Now i can also use MEF for doing internal decoupling / testabilty as with DI/IOC containers, but the MEF programming model is not optimized for that. This means if I try to do the OrderProcessing scenario above, it feels different than working with traditional IOC containers. I need to have Catalogs and Resolvers and such which are how MEF does things. It means that my classes need to be annotated with [Export] and [Import] attributes. Whilst if I use a DI container I simply register them in the container and they are autowired, end of story.

    MEF does a set of things that it was designed to do very well. It doesn't have to replace existing DI/IOC containers, it can complement them.


    Glenn Block - Program Manager - MEF
    Wednesday, August 06, 2008 8:35 AM