2012-09-08 49 views
4

对于我的应用程序,如this question描述我想使用MEF扫描可用的插件组件,然后存储所有的序列化格式可以导入和导出信息(例如一组字符串或内存流)。这是必要的,因为我需要通过AppDomain边界传输导入和导出信息而不是加载插件程序集(基本上我想延迟加载插件)。我发现了一些参考文献,例如this onethis one但没有链接给我任何想法如何:如何坚持MEF导入和导出信息到磁盘

  • 提取所有进口和出口从装配
  • 序列化所有必需的导入/导出信息
  • 然后再将水解后的序列化信息重新输入进口和出口。

我想我可以使用ReflectionModelServices class创建导入/导出定义,但仍然留下序列化和反序列化部分。任何人都可以向我指出一些例子,文档或提供有关如何执行这些步骤的建议?

回答

7

这回答这个问题是由凯文在MEF discussion list提供。事实证明,可以使用以下代码片段从MEF ExportDefinitionImportDefinition数据结构中提取所有必需的信息。

的第一步是将组件类型加载到目录。然后,对目录中的每个零件迭代导入和导出定义。导出定义只能放在类型,方法,属性和字段(我的代码目前忽略)上。所以要处理导出,可以使用下面的代码。

var exports = new List<Tuple<string, MemberInfo>>(); 
foreach (var export in part.ExportDefinitions) 
{ 
    var memberInfo = ReflectionModelServices.GetExportingMember(export); 
    Tuple<string, MemberInfo> exportDefinition = null; 
    switch (memberInfo.MemberType) 
    { 
     case MemberTypes.Method: 
      exportDefinition = new Tuple<string, MemberInfo>(export.ContractName, memberInfo.GetAccessors().First() as MethodInfo); 
      break; 
     case MemberTypes.NestedType: 
     case MemberTypes.TypeInfo: 
      exportDefinition = new Tuple<string, MemberInfo>(export.ContractName, memberInfo.GetAccessors().First() as Type); 
      break; 
     case MemberTypes.Property: 
      // this is a bit ugly because we assume that the underlying methods for a property are named as: 
      // get_PROPERTYNAME and set_PROPERTYNAME. In this case we assume that exports always 
      // have a get method. 
      var getMember = memberInfo.GetAccessors().Where(m => m.Name.Contains("get_")).First(); 
      var name = getMember.Name.Substring("get_".Length); 
      var property = getMember.DeclaringType.GetProperty(name); 
      exportDefinition = new Tuple<string, MemberInfo>(export.ContractName, property); 
      break; 

     default: 
      throw new NotImplementedException(); 
    } 

    exports.Add(exportDefinition); 
} 

为了处理的进口,这只能放置在性质,参数和字段(其再次忽略)以下代码可用于:

public void ExtractImports() 
{ 
    var imports = new List<Tuple<string, string>>(); 
    foreach (var import in part.ImportDefinitions) 
    { 
     SerializedImportDefinition importDefinition = !ReflectionModelServices.IsImportingParameter(import) 
      ? importDefinition = CreatePropertyImport(import) 
      : importDefinition = CreateConstructorParameterImport(import); 
    } 
} 

private Tuple<string, string> CreatePropertyImport(ImportDefinition import) 
{ 
    var memberInfo = ReflectionModelServices.GetImportingMember(import); 
    if (memberInfo.MemberType != MemberTypes.Property) 
    { 
     throw new ArgumentOutOfRangeException("import"); 
    } 

    // this is a bit ugly because we assume that the underlying methods for a property are named as: 
    // get_PROPERTYNAME and set_PROPERTYNAME. In this case we assume that imports always 
    // have a set method. 
    var getMember = memberInfo.GetAccessors().Where(m => m.Name.Contains("set_")).First(); 
    var name = getMember.Name.Substring("set_".Length); 
    var property = getMember.DeclaringType.GetProperty(name); 
    return new Tuple<string, string>(import.ContractName, property.ToString()); 
} 

private Tuple<string, string> CreateConstructorParameterImport(ImportDefinition import) 
{ 
    var parameterInfo = ReflectionModelServices.GetImportingParameter(import); 
    return new Tuple<string, string>(import.ContractName, parameterInfo.Value.ToString()); 
} 

注意,似乎导入不能通过方法参数提供,所以上面的代码不支持这些。

一旦所有的出口和进口都已经被处理是序列化存储MemberInfo对象转换成字符串格式的一个简单的问题。

+1

非常好的工作。我前段时间正在寻找类似于我的一些问题中看到的类似内容,很快我会再次访问此功能。感谢分享并花时间回答你自己的问题。 –

+0

'memberInfo'来自'memberInfo.GetAccessors()'哪里? – l33t

+0

@ l33t我更新了代码示例以包含获取'memberInfo'对象的部分。我希望这更清楚。感谢您指出缺少的代码行。 – Petrik

0

我有同样的需求,并最终实现非常相似,@Petrik在他的回答提到的东西。我的LazyAssemblyLoading solution在GitHub上可用。