0

In Funq and probably most other IoC containers I can simply do this to configure a type:

container.Register<ISomeThing>(c => new SomeThing());

How could I quickly extend MEF (or use existing MEF functionality) to do the same without using attributes.

Here is how I thought I could do it:

var container = new CompositionContainer();
var batch = new CompositionBatch();
batch.AddExport<ISomeThing>(() => new SomeThing());
batch.AddExportedValue(batch);
container.Compose(batch);

With this extension method for CompositionBatch:

public static ComposablePart AddExport<TKey>(this CompositionBatch batch, Func<object> func)
{
    var typeString = typeof(TKey).ToString();
    return batch.AddExport(
        new Export(
            new ExportDefinition(
                typeString, 
                new Dictionary<string, object>() { { "ExportTypeIdentity", typeString } }),
            func));

}

If I later do:

var a = container.GetExport<ISomeThing>().Value;
var b = container.GetExport<ISomeThing>().Value;

Both instance are the same. How can I force (configure) them to be different instances?

If this is not the way to go, how would I do this in MEF?

bitbonk
  • 48,890
  • 37
  • 186
  • 278

4 Answers4

1

I would imagine the key is to add the delegate to the container, e.g.:

container.AddExportedValue<Func<ISomething>>(() => new Something());

That way you can grab the delegate and execute it:

var factory = container.GetExport<Func<ISomething>>();
ISomething something = factory();

Of course, MEF (Silverlight) does provide a native ExportFactory<T> (and ExportFactory<T,TMetadata> type that supports the creation of new instances for each call to import. You can add support for this by downloading Glen Block's ExportFactory for .NET 4.0 (Desktop) library.

Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129
  • Do you have an example on how to use ExportFactory in my scenario (explicit configuration instead with attributes)? – bitbonk Sep 09 '11 at 13:36
  • Hm, in that Skydrive folder you linkes I found something that seems much more simpler and lightweight than ExportFactory: `FuncCatalog`: https://skydrive.live.com/self.aspx/blog/FuncCatalogExtensions.zip?cid=f8b2fd72406fb218&sc=documents – bitbonk Sep 09 '11 at 13:38
1

If you don't want to use attributes, you can use this trick (based on Mark Seemann's blogpost).

First, create a generic class like this:

[PartCreationPolicy(CreationPolicy.NonShared)]
public class MefAdapter<T> where T : new()
{
    private readonly T export;

    public MefAdapter()
    {
        this.export = new T();
    }

    [Export]
    public virtual T Export
    {
        get { return this.export; }
    }
}

Now you can register any class you want in the container, like this:

var registeredTypesCatalog = new TypeCatalog(
    typeof(MefAdapter<Foo>),
    typeof(MefAdapter<Bar>), 
    ...);
var container = new CompositionContainer(catalog);

Alternatively, you could implement your own export provider derived from ExportProvider, which allows you to pretty much duplicate Funq's way of working:

var provider = new FunqyExportProvider();
provider.Register<IFoo>(context => new Foo());
var container = new CompositionContainer(provider);
Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
  • +1 That is a nice trick! But now that I think of it, a nice feature would also be `funcCatalog.AddPart( exportPovider => new SomeThing(ep.GetExportedObject()));` This was stolen form the FuncCatalog from my answer. Resolving addtional dependencies is not possible with your approach, right? – bitbonk Sep 09 '11 at 14:31
  • @bitbonk: Mark Seemann's original post (which I linked to) shows how you can adapt this trick to resolve additional dependencies. – Wim Coenen Sep 21 '11 at 13:24
0

Both instance are the same. How can I force (configure) them to be different instances?

Simply mark the SomeThing class like this:

[Export(typeof(ISomeThing)]
[PartCreationPolicy(CreationPolicy.NonShared]
public class SomeThing : ISomeThing
{
   ...
}

And then you will get different instances wherever you import ISomeThing.

Alternatively, you can also set a required creation policy on an import:

[Export(typeof(IFoo))]
public class Foo : IFoo
{
   [Import(typeof(ISomeThing), 
       RequiredCreationPolicy = CreationPolicy.NonShared)]
   public ISomething SomeThing { private get; set; }

}
Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
  • The idea behind my question is to *not* use Attributes for configuration at all. – bitbonk Sep 09 '11 at 13:48
  • I suspected as much but it wasn't entirely clear from your question whether you knew about the "normal MEF way" of doing this. I'll post another answer that deals with your requirement to avoid attributes. – Wim Coenen Sep 09 '11 at 13:53
0

In Glen Block's Skydrive directory linked to in Matthew Abbott's answer I found something that seems simple and lightweight: A FuncCatalog. Download it here: FuncCatalogExtension.

Using the few little classes from that project I could now do this:

var funcCatalog = new FuncCatalog();
funcCatalog.AddPart<ISomeThing>(ep => new SomeThing());
var container = new CompositionContainer(funcCatalog);
var batch = new CompositionBatch();
batch.AddExportedObject<ExportProvider>(container);
container.Compose(batch);

var a = container.GetExportedObject<ISomeThing>();
var b = container.GetExportedObject<ISomeThing>();
Community
  • 1
  • 1
bitbonk
  • 48,890
  • 37
  • 186
  • 278
  • Glen's version was for and old preview version of MEF and does not compile anymore. I ported it to .NET 4.0 and it *seems* to work so far: https://github.com/bitbonk/FuncContainerExtension – bitbonk Sep 09 '11 at 22:58