在未将遗留对象归因为MEF中的导出时,可以导入旧对象

时间:2010-01-08 21:45:55

标签: .net mef

我开始使用MEF构建基于插件的应用程序,我正在慢慢添加MEF。有很多现有的代码还没有任何MEF DNA,但我仍然希望将代码放入由合成自动创建的新对象中。

让我们具体化。

我有一个实现IFoo接口的对象列表,并以特定但有用的方式对应用程序模型进行操作。

interface IFooCool : IFoo {}

class FooCool : IFooCool {...}

interface IFooAwesome : IFoo {}

class FooAwesome : IFooAwesome {}

IEnumerable<IFoo> fooCollection = ProvidedTheOldFashionWay(not, yet, MEF);

现在,我想创建一些有用的工具,将IFooX接口映射到各种用户操作,如菜单命令或按钮点击。

[Export(ITool)]
class CoolTool : ITool
{
    IFooCool _fooCool;
    [ImportingConstructor]
    CoolTool(IFooCool fooCool) 
    {
        _fooCool = fooCool;
    }

    [Export(MenuAction)]
    void DoSomething() { _fooCool.SomeWork(...); }
}

这是我想做的事情:

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);

CoolTool将被实例化,FooCool旧版对象将被传递给它。然后我可以获得导出的功能并在菜单中很好地显示它们然后我们就去了。当用户单击某个菜单项时,新的CoolTool将使用IFooCool界面的现有功能来执行某些操作,很酷。

当然,这不起作用。由于旧对象未归类为导出,因此将它们添加到合成批处理中并没有帮助。在上面的代码中,我使用batch.AddPart(object)而不是batch.AddPart(ComposablePart)将foo实例添加到批处理中。第一种方法使用属性模型从对象中发现可组合信息。

如何使用第二次过载?我可以动态地将现有的非MEF对象包装在ComposablePart中吗?类似的东西:

batch.AddPart(CreateComposablePart(typeof(IFooCool), foo));

BTW,我在非silverlight应用程序中使用预览8。

3 个答案:

答案 0 :(得分:1)

还有一种方法 - 排序。

batch.AddExportedValue(typeof(IFooCool).Fullname, (IFooCool)foo);

不幸的是,问题比这复杂得多。 foo我需要标记为出口实际上是这样的:

Dictionary<Type, IFoo> _registeredFoos;

IFooCool      => FooCool
IFooAwesome   => FooAwesome

并且,以下(没有IFooCool强制转换)不起作用:

batch.AddExportedValue(typeof(IFooCool).Fullname, foo);

所以我真的必须像这样循环起来:

foreach(var entry in _registeredFoos)
{
    batch.AddExportedValue(entry.Key.Fullname, // that was easy...
                          (?)entry.Value);     // what?  This is a generic method...
}

好吧,我只是打开源代码,看看发生了什么。好的解决方案吧?了解并利用框架功能的内部细节始终是开发应用程序的健康方式。我会这样做:

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);
}

不确定。现在我需要洗个澡。

答案 1 :(得分:1)

看起来您正在尝试调用此扩展方法:

AttributedModelServices.AddExportedValue<T>(
   this CompositionBatch batch,
   string contractName,
   T exportedValue);

您的问题是您在运行时只知道类型参数T;这是你词典的关键。一种解决方案可能是使用反射来调用方法,这使您可以在运行时填充类型参数。首先,获取通用方法的MethodInfo,如下所示:

MethodInfo genericAddExportedValue = 
   typeof(AttributedModelServices).GetMethods()
   .Where(x=>x.Name == "AddExportedValue")
   .First(x=>x.GetParameters().Count() == 3);

现在您可以关闭循环中的type参数并调用方法:

foreach(var entry in _registeredFoos)       
{       
    MethodInfo addExportedValue = 
       genericAddExportedValue.MakeGenericMethod(entry.Key);
    addExportedValue.Invoke(
       null,
       new object[] {batch, entry.Key.FullName, entry.Value});
}

或者,您可以创建抽象ExportProvider类的实现,该类知道如何使用_registeredFoos字典来提供导出。但这可能需要做更多的工作。

答案 2 :(得分:0)

要将没有属性的旧类用作MEF部分,可以使用属于MEFContrib的ConfigurableDefinitionProvider。这允许您使用配置文件而不是属性来定义导入和导入。

(你自己的答案澄清了你的问题实际上是如何添加你已经作为Dictionary<Type,object>提供的部分,但我认为回答问题标题提出的更简单的问题可能也很有趣。 )