Search code examples
.netdependenciesmefcomposition

Attribute like InheritedExport in Constructor in MEF


I am developing an open ended application and I am new to MEF. I need to hide MEF totally from derived classes. So here is my scenerio.

I have a BaseAssembly

public class ListContainer
{
    [ImportMany(typeof(IBase))]
    public List<IBase> MyObjects { get; set; }

    public void AssembleDriverComponents()
{
     .... Some code to create catalogue..
     //Crete the composition container
        var container = new CompositionContainer(aggregateCatalog);

        // Composable parts are created here i.e. the Import and Export components assembles here
        container.ComposeParts(this);
}
}

 [InheritedExport(typeof(IBase))]
public abstract class Base : IBase
{
    private IInfoBase infoBase;

    //This is something which I want to do. If I have a derived class from Base. 
      Then It does not need to use ImportingConstructor.
    [ImportingConstructor()]
    public Base(InfoBase nfoBase)
    {
        this.infoBase = infoBase;
    }
}

[InheritedExport(typeof(IInfoBase))]
public interface IInfoBase
{
    string Category { get; set; }
}


public class InfoBase : IInfoBase
{

    public string Category
    {
        get;
        set;
    }
}

Other assemblies will refer to base assembly.

ReferenceAssembly will have

public class Derived : Base
{
    public Derived(BaseInfo info)
        : base(info)
    {
        info.Category = "CategoryA";
    }
}

In this case MEF is not creating the object for derived one.

In summary, I need something like InheritedExport for ImportingConstructor also.


Solution

  • You can do this using MEF2. MEF2 introduces the Convention-Based Programming Model which can replace or complement the "Attributed Programming Model".

    For the interface IBase:

    public interface IBase { }
    

    The abstract base class Base:

    [InheritedExport(typeof(IBase))]
    public abstract class Base : IBase
    {
        private IInfoBase infoBase;
    
        //No ImportingConstructorAttribute. This will be set with conventions.
        public Base(IInfoBase infoBase)
        {
            this.infoBase = infoBase;
        }
    }
    

    A couple of Base implementations:

    public class Derived : Base
    {        
        public Derived(IInfoBase info)
            : base(info)
        {
            info.Category = "CategoryA";
        }
    }
    
    public class AnotherDerived : Base
    {
        public AnotherDerived(IInfoBase info)
            : base(info)
        {
            info.Category = "CategoryB";
        }
    }
    

    The info interface is unchanged:

    [InheritedExport(typeof(IInfoBase))]    
    public interface IInfoBase
    {
        string Category { get; set; }
    }
    

    Added the PartCreationPolicyAttribute so that the exported parts are not shared between the implementations of Base. This doesn't matter though.

    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class InfoBase : IInfoBase
    {
        public string Category
        {
            get;
            set;
        }
    }
    

    And finally the ListContainer:

    public class ListContainer
    {
        [ImportMany(typeof(IBase))]
        public List<IBase> MyObjects { get; set; }
    
        public void AssembleDriverComponents()
        {
            var regBuilder = new RegistrationBuilder();
            //SelectConstructor is the equivalent of the ImportingConstructorAttribute.
            //Note that my approach here is very crude. Simply use the first constructor.
            regBuilder.ForTypesDerivedFrom<Base>().SelectConstructor(ctors => ctors.First());
    
            //Only an AssemblyCatalog for this example. Note that the registration 
            //builder is used here.
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), regBuilder);
            //Crete the composition container
            var container = new CompositionContainer(catalog);
    
            // Composable parts are created here i.e. the Import and Export components assembles here
            container.SatisfyImportsOnce(this);
            System.Diagnostics.Debug.Assert(this.MyObjects.Count == 2);
        }
    }
    

    More info on MEF Conventions: MEF 2 article series and Getting started with convention-based part registration in MEF 2