程序集/目录的延迟加载

5
我的应用程序由几个核心程序集和几个扩展/插件程序集组成。为了让MEF知道插件提供的所有部分,即使我永远不会使用它们的任何部分,我也必须加载这些程序集。这使得应用程序启动需要更多时间(如果我将所有程序集加载到启动时),并且还增加了内存占用。
理想情况下,我只有在实际需要它们时才需要加载这些程序集。我只会加载插件的导出数据,并且当我实际需要导入一部分时,MEF会加载程序集并提供该部分。
我发现有一个东西几乎做了我刚才写的一切,但在询问了关于它的问题之后,在MEF CachedAssemblyCatalog - Lazy Loading of Assemblies中,我意识到这段代码并不稳定,而且MEF团队也没有维护它,所以我决定不使用它。
我的问题是如何实现这种行为:
- 能够访问插件程序集的导出元数据而无需加载其整个程序集。 - 与导入部分的代码进行透明集成;即像往常一样使用导入 - 其他人(专门目录?)将负责在必要时加载程序集并提供所请求的部分。 - 不失去任何现有的MEF功能,如重新组合,懒加载等。
我完全可以接受需要预先解析插件以创建元数据程序集、XML文件或其他东西的解决方案。
2个回答

2
如果你只是想要延迟加载程序集,那么你可以参考这个问题的部分解决方案。你不需要获取该解决方案中检索到的所有信息,可能只需要合同名称、程序集名称以及该部件是否正在导出或导入合同。然后,你可以编写一个目录,根据需要加载程序集,例如像这样:
public sealed class DelayLoadingCatalog : ComposablePartCatalog
{
    // List containing tuples which have the 'contract name' 
    // and the 'assembly name'
    private readonly List<Tuple<string, string>> m_Plugins 
        = new List<Tuple<string, string>>();
    private readonly Dictionary<string, AssemblyCatalog> m_Catalogs 
        = new Dictionary<string, AssemblyCatalog>();

    public DelayLoadingCatalog(IEnumerable<Tuple<string, string>> plugins)
    {
        m_Plugins.AddRange(plugins);
    }

    public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
    {
        var partsToLoad = m_Plugins
            .Where(t => t.Item1.Equals(definition.ContractName));
        foreach (var part in partsToLoad)
        {
            if (!m_Catalogs.ContainsKey(part.Item2.Name))
            {
                var assembly = Assembly.Load(new AssemblyName(part.Item2.Name));
                m_Catalogs.Add(part.Item2.Name, new AssemblyCatalog(assembly));
            }
        }

        return m_Catalogs.SelectMany(p => p.Value.GetExports(definition));
    }

    public override IQueryable<ComposablePartDefinition> Parts
    {
        get 
        { 
            throw new NotImplementedException(); 
        }
    }
}

您可以像这样使用它。
class Program
{
    public void Init()
    {
        var domainSetup = new AppDomainSetup
        {
            ApplicationBase = Directory.GetCurrentDirectory(),
        };

        var scanDomain = AppDomain.CreateDomain(
            "scanDomain", 
            null, 
            domainSetup);
        var scanner = scanDomain.CreateInstanceAndUnwrap(
            typeof(MyScanner).Assembly.FullName, 
            typeof(MyScanner).FullName) as MyScanner;
        var plugins = scanner.Scan(myPluginsPath);

        // Make sure we don't have the assemblies loaded anymore ...
        AppDomain.Unload(scanDomain);

        var catalog = new DelayLoadingCatalog(plugins);
        var container = new CompositionContainer(catalog);

        container.ComposeParts(this);
    }

    [Import("MyCoolExport")]
    public object MyImport
    {
        get;
        set;
    }
}

示例DelayLoadCatalog并不太聪明,因为它将继续在Tuple列表中进行搜索。优化代码并不难。例如,你可以检查是否已加载了所有程序集,并在那一点停止搜索该列表。


嘿,Petrik,非常感谢你的帮助! 我有一段时间前就偶然发现了你链接到的问题,它对我解决这个问题帮助很大。 最终我采用了与你描述的类似的方法 - 将数据序列化到文件中并创建一个新目录,该目录消耗该文件。 这样,所有包括元数据的导出都可用,并且使用 ReflectionModelServices API,一切都是懒加载的。 对于我的要求来说,这似乎是最好的方式。 我希望我有时间回答这个问题,并展示出我已经完成的工作。 再次感谢! - Adi Lester

0
如果有人感兴趣的话,我自己实现了一个解决方案,并最近在GitHub上发布了。LazyAssemblyLoading解决方案允许用户将程序集的部分信息序列化,然后稍后使用它来初始化LazyAssemblyCatalog。当需要加载程序集的某个部分时,只会在此时才加载该程序集,而在程序集未加载时仍然可以像往常一样使用其元数据。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接