ExportProvider.GetExportFactory<T>()

Jan 30, 2012 at 10:55 AM

Hi!

MEF2 introduces handy ExportFactory<T> class to support dynamic part instantiation.
However, there are no GetExportFactory<T> and GetExportFactories<T> methods in ExportProvider:

public abstract partial class ExportProvider
{
    // T
    public T GetExportedValue<T>();
    public T GetExportedValue<T>(string contractName);
    public IEnumerable<T> GetExportedValues<T>();
    public IEnumerable<T> GetExportedValues<T>(string contractName);

    // Lazy<T>
    public Lazy<T> GetExport<T>();
    public Lazy<T> GetExport<T>(string contractName);
    public IEnumerable<Lazy<T>> GetExports<T>();
    public IEnumerable<Lazy<T>> GetExports<T>(string contractName);

    // ExportFactory<T> — no such methods exist
    public ExportFactory<T> GetExportFactory<T>();
    public ExportFactory<T> GetExportFactory<T>(string contractName);
    public IEnumerable<ExportFactory<T>> GetExportFactories<T>();
    public IEnumerable<ExportFactory<T>> GetExportFactories<T>(string contractName);
}

I can currently write things like this: var lazyExport = myContainer.GetExport<IMyService>("test");
and I'd like to create factories in a similar way: var factory = myContainer.GetExportFactory<IMyService>("test"); 

I can emulate parameterless GetExportFactory/GetExportFactories method overloads
using extension methods for CompositionContainer class, but overloads with contractName
parameter are not that easy to emulate:

public static class CompositionContainerExtensions
{
	public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container)
	{
		var helper = new ImportHelper<T>();
		container.SatisfyImportsOnce(helper);
		return helper.ImportedValue;
	}

	public static IEnumerable<ExportFactory<T>> GetExportFactories<T>(this CompositionContainer container)
	{
		var helper = new ImportManyHelper<T>();
		container.SatisfyImportsOnce(helper);
		return helper.ImportedValues;
	}

	private class ImportHelper<T>
	{
		[Import]
		public ExportFactory<T> ImportedValue { get; set; }
	}

	private class ImportManyHelper<T>
	{
		[ImportMany]
		public IEnumerable<ExportFactory<T>> ImportedValues { get; set; }
	}

	public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container, string contractName)
	{
		// cannot use similar approach: Import attribute parameters are constant
	}

	public static IEnumerable<ExportFactory<T>> GetExportFactories<T>(this CompositionContainer container, string contractName)
	{
		// cannot use similar approach: ImportMany attribute parameters are constant
	}
}

Is it possible to add ExportProvider.GetExportFactory/GetExportFactories methods to the upcoming release?
If not, what approach should I use instead?

Jan 30, 2012 at 4:57 PM

Hi, and thanks for the feedback!

We don’t currenlty plan on adding this to the container/ExportProvider API. In general we are trying to move away as much as possible from “Service Locator” style usage of the MEF container.

Parts built using this style – calling the container directly – are harder to maintain and test, and from a MEF implementation standpoint can’t take advantage of the diagnostic and performance advantages available to parts using constructor injection (via the ImportingConstructor attribute).

The recommended alternative is to [Import] ExportFactory<T> wherever it is required. If the required type parameter is not available up-front, encapsulating the need for an ExportFactory<T> into a helper type as you have done is the best workaround.

Hope this helps,

Nick

From: yallie [email removed]
Sent: Monday, January 30, 2012 2:55 AM
To: Nicholas Blumhardt
Subject: ExportProvider.GetExportFactory<T>() [MEF:287868]

From: yallie

Hi!

MEF2 introduces handy ExportFactory<T> class to support dynamic part instantiation.
However, there are no GetExportFactory<T> and GetExportFactories<T> methods in ExportProvider:

public abstract partial class ExportProvider
{
    // T
    public T GetExportedValue<T>();
    public T GetExportedValue<T>(string contractName);
    public IEnumerable<T> GetExportedValues<T>();
    public IEnumerable<T> GetExportedValues<T>(string contractName);
 
    // Lazy<T>
    public Lazy<T> GetExport<T>();
    public Lazy<T> GetExport<T>(string contractName);
    public IEnumerable<Lazy<T>> GetExports<T>();
    public IEnumerable<Lazy<T>> GetExports<T>(string contractName);
 
    // ExportFactory<T> — no such methods exist
    public ExportFactory<T> GetExportFactory<T>();
    public ExportFactory<T> GetExportFactory<T>(string contractName);
    public IEnumerable<ExportFactory<T>> GetExportFactories<T>();
    public IEnumerable<ExportFactory<T>> GetExportFactories<T>(string contractName);
}

I can currently write things like this: var lazyExport = myContainer.GetExport<IMyService>("test");
and I'd like to create factories in a similar way: var factory = myContainer.GetExportFactory<IMyService>("test");

I can emulate parameterless GetExportFactory/GetExportFactories method overloads
using extension methods for CompositionContainer class, but overloads with contractName
parameter are not that easy to emulate:

public static class CompositionContainerExtensions
{
        public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container)
        {
               var helper = new ImportHelper<T>();
               container.SatisfyImportsOnce(helper);
               return helper.ImportedValue;
        }
 
        public static IEnumerable<ExportFactory<T>> GetExportFactories<T>(this CompositionContainer container)
        {
               var helper = new ImportManyHelper<T>();
               container.SatisfyImportsOnce(helper);
               return helper.ImportedValues;
        }
 
        private class ImportHelper<T>
        {
               [Import]
               public ExportFactory<T> ImportedValue { get; set; }
        }
 
        private class ImportManyHelper<T>
        {
               [ImportMany]
               public IEnumerable<ExportFactory<T>> ImportedValues { get; set; }
        }
 
        public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container, string contractName)
        {
               // cannot use similar approach: Import attribute parameters are constant
        }
 
        public static IEnumerable<ExportFactory<T>> GetExportFactories<T>(this CompositionContainer container, string contractName)
        {
               // cannot use similar approach: ImportMany attribute parameters are constant
        }
}

Is it possible to add ExportProvider.GetExportFactory/GetExportFactories methods to the upcoming release?
If not, what approach should I use instead?

Jan 30, 2012 at 11:25 PM

Hi Nick,

thanks for your reply!

We don’t currenlty plan on adding this to the container/ExportProvider API. In general we are trying to move away as much as possible from “Service Locator” style usage of the MEF container.

Parts built using this style – calling the container directly – are harder to maintain and test, and from a MEF implementation standpoint can’t take advantage of the diagnostic and performance advantages available to parts using constructor injection (via the ImportingConstructor attribute).

I agree that using the container in such a manner is a bad idea :)

But I am writing a library that is tightly coupled with MEF itself — http://zyan.codeplex.com
It contains composable part catalog (ZyanCatalog) which exposes components published over .NET Remoting.
Remote dispatcher needs a simple way to get components from the attached CompositionContainer.
It cannot use [Import] because it doesn't know anything about the components at compile-time. 

Is there a way to get ExportFactory<T> from CompositionContainer when both contract name and type T are not known at compile time?

Jan 31, 2012 at 12:01 AM

Hi,

There’s probably some experimentation involved - I believe you can emulate the behavior you need by:

1. Construct a ContractBasedImportDefinition for the product (T) that you require. Set the RequiredCreationPolicy to NonShared

2. Construct an ExportFactory<T> manually, using its public constructor, that

a. Wraps a call to CompositionContainer.GetExport()for creation, using the import definition from (1)

b. Wraps a call to CompositionContainer.ReleaseExport() for release

I hope this helps get you on the right track, I can’t guarantee there aren’t any road blocks waiting down the track though, it isn’t a common scenario J

Cheers,

Nick

From: yallie [email removed]
Sent: Monday, January 30, 2012 3:25 PM
To: Nicholas Blumhardt
Subject: Re: ExportProvider.GetExportFactory<T>() [MEF:287868]

From: yallie

Hi Nick,

thanks for your reply!

We don’t currenlty plan on adding this to the container/ExportProvider API. In general we are trying to move away as much as possible from “Service Locator” style usage of the MEF container.

Parts built using this style – calling the container directly – are harder to maintain and test, and from a MEF implementation standpoint can’t take advantage of the diagnostic and performance advantages available to parts using constructor injection (via the ImportingConstructor attribute).

I agree that using the container in such a manner is a bad idea :)

But I am writing a library that is tightly coupled with MEF itself — http://zyan.codeplex.com
It contains composable part catalog (ZyanCatalog) which exposes components published over .NET Remoting.
Remote dispatcher needs a simple way to get components from the attached CompositionContainer.
It cannot use [Import] because it doesn't know anything about the components at compile-time.

Is there a way to get ExportFactory<T> from CompositionContainer when both contract name and type T are not known at compile time?

Jan 31, 2012 at 11:44 AM

Hi Nick,

thanks a lot for your suggestion!

There’s probably some experimentation involved - I believe you can emulate the behavior you need by:
1. Construct a ContractBasedImportDefinition for the product (T) that you require. Set the RequiredCreationPolicy to NonShared
2. Construct an ExportFactory<T> manually, using its public constructor, that
a. Wraps a call to CompositionContainer.GetExport() for creation, using the import definition from (1)
b. Wraps a call to CompositionContainer.ReleaseExport() for release
 
I hope this helps get you on the right track, I can’t guarantee there aren’t any road blocks waiting down the track though, it isn’t a common scenario

Frankly, I was hoping that I won't be forced to cope with raw import definitions :)
But it turned out to be not that difficult:

public static class CompositionContainerExtensions
{
  public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container)
  {
    return GetExportFactory<T>(container, null);
  }

  public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container, string contractName)
  {
    var importDefinition = new ContractBasedImportDefinition(
      contractName ?? AttributedModelServices.GetContractName(typeof(T)),
      AttributedModelServices.GetTypeIdentity(typeof(T)),
      null, // no metadata required
      ImportCardinality.ExactlyOne,
      false, // non-recomposable
      false, // not a pre-requisite
      CreationPolicy.NonShared);

    return new ExportFactory<T>(() =>
    {
      var export = container.GetExports(importDefinition).FirstOrDefault();
      var value = (T)export.Value;

      return Tuple.Create<T, Action>(value, () => container.ReleaseExport(export));
    });
  }
}

// Usage:

interface ITest { }

[Export(typeof(ITest))]
class Test : ITest, IDisposable
{
  public void Dispose()
  {
    Trace.WriteLine("Test class is disposed!");
  }
}

[TestMethod]
public void TestExportFactory()
{
  var catalog = new TypeCatalog(typeof(ITest), typeof(Test), typeof(User));
  var container = new CompositionContainer(catalog);

  var factory = container.GetExportFactory<ITest>();
  using (var export = factory.CreateExport())
  {
    var test = export.Value;
    Assert.IsNotNull(test);
  }
}

Jan 31, 2012 at 4:47 PM

Awesome – thanks for following up with the example, too!

Nick

From: yallie [email removed]
Sent: Tuesday, January 31, 2012 3:45 AM
To: Nicholas Blumhardt
Subject: Re: ExportProvider.GetExportFactory<T>() [MEF:287868]

From: yallie

Hi Nick,

thanks a lot for your suggestion!

There’s probably some experimentation involved - I believe you can emulate the behavior you need by:
1. Construct a ContractBasedImportDefinition for the product (T) that you require. Set the RequiredCreationPolicy to NonShared
2. Construct an ExportFactory<T> manually, using its public constructor, that
a. Wraps a call to CompositionContainer.GetExport() for creation, using the import definition from (1)
b. Wraps a call to CompositionContainer.ReleaseExport() for release

I hope this helps get you on the right track, I can’t guarantee there aren’t any road blocks waiting down the track though, it isn’t a common scenario

Frankly, I was hoping that I won't be forced to cope with raw import definitions :)
But it turned out to be not that difficult:

public static class CompositionContainerExtensions
{
  public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container)
  {
    return GetExportFactory<T>(container, null);
  }
 
  public static ExportFactory<T> GetExportFactory<T>(this CompositionContainer container, string contractName)
  {
    var importDefinition = new ContractBasedImportDefinition(
      contractName ?? AttributedModelServices.GetContractName(typeof(T)),
      AttributedModelServices.GetTypeIdentity(typeof(T)),
      null, // no metadata required
      ImportCardinality.ExactlyOne,
      false, // non-recomposable
      false, // not a pre-requisite
      CreationPolicy.NonShared);
 
    return new ExportFactory<T>(() =>
    {
      var export = container.GetExports(importDefinition).FirstOrDefault();
      var value = (T)export.Value;
 
      return Tuple.Create<T, Action>(value, () => container.ReleaseExport(export));
    });
  }
}
 
// Usage:
 
interface ITest { }
 
[Export(typeof(ITest))]
class Test : ITest, IDisposable
{
  public void Dispose()
  {
    Trace.WriteLine("Test class is disposed!");
  }
}
 
[TestMethod]
public void TestExportFactory()
{
  var catalog = new TypeCatalog(typeof(ITest), typeof(Test), typeof(User));
  var container = new CompositionContainer(catalog);
 
  var factory = container.GetExportFactory<ITest>();
  using (var export = factory.CreateExport())
  {
    var test = export.Value;
    Assert.IsNotNull(test);
  }
}