2012-09-08 103 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上可用。