Reputation: 503
I'm starting to use MEF to build up a plugin based application, and I'm slowly adding MEF to the mix. There is a lot of existing code that does not have any MEF DNA yet, but I still want to get that code into the new objects that are being automatically created by composition.
Let's make this concrete.
I have a list of objects that implement the IFoo interface and operate on the application model in specific but useful ways.
interface IFooCool : IFoo {}
class FooCool : IFooCool {...}
interface IFooAwesome : IFoo {}
class FooAwesome : IFooAwesome {}
IEnumerable<IFoo> fooCollection = ProvidedTheOldFashionWay(not, yet, MEF);
Now, I want to create some useful tools that map the IFooX
interfaces to various user actions like menu commands or button clicks.
[Export(ITool)]
class CoolTool : ITool
{
IFooCool _fooCool;
[ImportingConstructor]
CoolTool(IFooCool fooCool)
{
_fooCool = fooCool;
}
[Export(MenuAction)]
void DoSomething() { _fooCool.SomeWork(...); }
}
Here's what I'd like to do:
var batch = new CompositionBatch();
foreach(var foo in fooCollection)
{
batch.AddPart(foo); //add those legacy objects to the batch
}
var catalog = new TypeCatalog(typeof(CoolTool)); //or assembly or directory, ...
var container = new CompositionContainer(catalog);
container.Compose(batch);
The CoolTool
will be instantiated and the FooCool
legacy object will be passed to it. Then I can get the exported functions and display them nicely in the menu and off we go. When the user clicks a menu item, the new CoolTool
will use the existing functionality of the IFooCool
interface to do something, well, cool.
Of course, that doesn't work. Since the legacy objects are not attributed as exports, adding them to the composition batch does not help. In the code above, I'm adding the foo instances to the batch with batch.AddPart(object)
instead of batch.AddPart(ComposablePart)
. The first method uses the attributed model to discover composable information from the object.
How can I use the second overload? Can I wrap my existing non-MEF object in a ComposablePart on the fly? Something like:
batch.AddPart(CreateComposablePart(typeof(IFooCool), foo));
BTW, I'm use preview 8 in a non-silverlight app.
Upvotes: 3
Views: 633
Reputation: 66753
To use legacy classes without attributes as MEF parts, you can use the ConfigurableDefinitionProvider which is part of MEFContrib. This allows you to define the imports and imports with a configuration file instead of attributes.
(Your question as clarified by your own answer is actually how to add parts which you already have available as a Dictionary<Type,object>
, but I figured it might also be interesting to answer the simpler question suggested by the question title.)
Upvotes: 0
Reputation: 66753
It looks like you are trying to call this extension method:
AttributedModelServices.AddExportedValue<T>(
this CompositionBatch batch,
string contractName,
T exportedValue);
Your problem is that you only know the type parameter T
at runtime; it's the key of your dictionary. One solution could be to use reflection to call the method, which enables you to fill in type parameters at runtime. First, get the MethodInfo
of the generic method like this:
MethodInfo genericAddExportedValue =
typeof(AttributedModelServices).GetMethods()
.Where(x=>x.Name == "AddExportedValue")
.First(x=>x.GetParameters().Count() == 3);
Now you can close the type parameter inside your loop and invoke the method:
foreach(var entry in _registeredFoos)
{
MethodInfo addExportedValue =
genericAddExportedValue.MakeGenericMethod(entry.Key);
addExportedValue.Invoke(
null,
new object[] {batch, entry.Key.FullName, entry.Value});
}
Alternatively, you could create an implemention of the abstract ExportProvider
class which knows how to use your _registeredFoos
dictionary to provide exports. But that's probably a lot more work.
Upvotes: 1
Reputation: 503
And there is a way - sortof.
batch.AddExportedValue(typeof(IFooCool).Fullname, (IFooCool)foo);
Unfortunately, the problem is a little more complex than that. The foo's I need to mark as exports are actually in this:
Dictionary<Type, IFoo> _registeredFoos;
IFooCool => FooCool
IFooAwesome => FooAwesome
And, the following (without the IFooCool
cast) does not work:
batch.AddExportedValue(typeof(IFooCool).Fullname, foo);
So I really have to loop over the foos like this:
foreach(var entry in _registeredFoos)
{
batch.AddExportedValue(entry.Key.Fullname, // that was easy...
(?)entry.Value); // what? This is a generic method...
}
Well, I'll just crack open the source and look what's going on. Great solution right? Knowing and taking advantage of the internal details of a framework function is always a healthy way to develop your apps. I'll do this:
foreach(var entry in _registeredFoos)
{
string typeIdentity = AttributedModelServices.GetTypeIdentity(entry.Key);
IDictionary<string, object> metadata = new Dictionary<string, object>();
metadata.Add(CompositionConstants.ExportTypeIdentityMetadataName, typeIdentity);
Export export = new Export(entry.Key, metadata, () => entry.Value);
batch.AddExport(export);
}
Sure. Now I need to go take a shower.
Upvotes: 1