Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
322 views
in Technique[技术] by (71.8m points)

c# - How to persist MEF import and export information to disk

For my application as described in this question I want to use MEF to scan the available plugin assemblies and then store all the available import and export information in a serialized format (e.g. a set of strings or a memory stream). This is necessary because I need to transfer the import and export information over an AppDomain boundary without loading the plugin assemblies (essentially I want to delay load the plugins). I found some references, for instance this one or this one but none of the links gave me any idea how to:

  • Extract all the imports and exports from an assembly
  • Serialize all the required import/export information
  • And then later on re-hydrate the serialized information back into imports and exports.

I think I can use the ReflectionModelServices class to create Import/Export definitions but that still leaves the serialization and deserialization parts. Can anybody point me to some examples, documentation or provide me with suggestion as to how to go about these steps?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This answer to this question was provided by Kevin on the MEF discussion list. It turns out that one can extract all the required information from the MEF ExportDefinition and ImportDefinition data structures with the following code snippets.

The first step is to load the assembly types into a catalog. Then for each part in the catalog iterate over the import and export definitions. Export definitions can only be placed on types, methods, properties and fields (which my code ignores at the moment). So to process the exports the following code can be used.

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

In order to process the imports, which can only be placed on properties, parameters and fields (which are again ignored) the following code can be used:

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

Note that it seems that imports cannot be provided via method parameters so the code above does not support those.

Once all the exports and imports have been processed it is a simple question of serializing the stored MemberInfo objects into a string format.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...