MEF - 从嵌入式DLL获取程序集

7
我正在使用MEF为我的WPF应用程序创建“插件”。其中一些插件我想直接嵌入到EXE文件中,因为EXE需要独立运行。我正在使用Fody的Costura来嵌入资源以及所有其他引用。由于exe文件需要是独立的,我无法为这些插件创建目录并使用DirectoyCatalog。
是否有任何方法可以从嵌入的资源中加载程序集,或者仅指定程序集名称,例如:
catalog.Catalogs.Add(new AssemblyCatalog("My.Assembly.Name));

我曾尝试循环遍历清单资源,但这些资源似乎被Fody压缩了:

var resourceNames = GetType().Assembly.GetManifestResourceNames();
            foreach (var resourceName in resourceNames)

需要任何帮助/建议,感激不尽。

2个回答

5

好的,我使用下面的类(在https://github.com/Sebazzz/EntityProfiler/blob/master/src/UI/EntityProfiler.Viewer/AppBootstrapper.cs找到了该代码并根据自己的需要进行了修改)使其对我有用:

要使用它,您只需调用提取函数,该函数将在资源清单中查找任何Costura Zip文件并解压缩并注册它。

该函数返回与传入函数中的字符串匹配的所有程序集的字典。然后我遍历它们并添加到目录中,以供组合容器使用:

var assemblies = CosturaAssemblyExtractor.Extract(AppDomain.CurrentDomain, Assembly.GetExecutingAssembly(), "My.AssemblyName");
foreach (var assembly in assemblies)
{
    catalog.Catalogs.Add(new AssemblyCatalog(assembly.Value));
}
container = new CompositionContainer(catalog);

类:

public static class CosturaAssemblyExtractor
{
    public static Dictionary<string, Assembly> Extract(AppDomain OrigDomain, Assembly ExecutingAssembly, string AssemblyStartsWith)
    {
        //var currentDomain = origDomain;
        var assemblies = OrigDomain.GetAssemblies();

        var references = new Dictionary<string, Assembly>();

        var manifestResourceNames = ExecutingAssembly.GetManifestResourceNames().Where(x => {
            return x.ToUpper().StartsWith(("costura." + AssemblyStartsWith).ToUpper()) && x.ToUpper().EndsWith(".dll.zip".ToUpper());
        });

        foreach (var resourceName in manifestResourceNames)
        {
            var solved = false;
            foreach (var assembly in assemblies)
            {
                var refName = string.Format("costura.{0}.dll.zip", GetDllName(assembly, true));
                if (resourceName.Equals(refName, StringComparison.OrdinalIgnoreCase))
                {
                    references[assembly.FullName] = assembly;
                    solved = true;
                    break;
                }
            }

            if (solved)
                continue;

            using (var resourceStream = ExecutingAssembly.GetManifestResourceStream(resourceName))
            {
                if (resourceStream == null) continue;

                if (resourceName.EndsWith(".dll.zip"))
                {
                    using (var compressStream = new DeflateStream(resourceStream, CompressionMode.Decompress))
                    {
                        var memStream = new MemoryStream();
                        CopyTo(compressStream, memStream);
                        memStream.Position = 0;

                        var rawAssembly = new byte[memStream.Length];
                        memStream.Read(rawAssembly, 0, rawAssembly.Length);
                        var reference = Assembly.Load(rawAssembly);
                        references[reference.FullName] = reference;
                    }
                }
                else
                {
                    var rawAssembly = new byte[resourceStream.Length];
                    resourceStream.Read(rawAssembly, 0, rawAssembly.Length);
                    var reference = Assembly.Load(rawAssembly);
                    references[reference.FullName] = reference;
                }
            }
        }
        return references;
    }

    private static void CopyTo(Stream source, Stream destination)
    {
        var array = new byte[81920];
        int count;
        while ((count = source.Read(array, 0, array.Length)) != 0)
        {
            destination.Write(array, 0, count);
        }
    }

    private static string GetDllName(Assembly assembly, bool withoutExtension = false)
    {
        var assemblyPath = assembly.CodeBase;
        return withoutExtension ? Path.GetFileNameWithoutExtension(assemblyPath) : Path.GetFileName(assemblyPath);
    }
}

1
当前的Costura版本似乎附加“compressed”而不是“zip”。无论如何,与其至少进行第二次检查EndsWith(".dll.zip"),最好直接尝试DeflateStream,然后在出现错误时将原始内容复制。此外,Stream已经具有CopyTo()方法。 - Zeus
@user1207289 不确定。你传递的是执行程序集吗?因为在从MSTest调用时可能会有所不同。 - Danhol86
@user1207289,你有示例代码可以让我参考吗? - Danhol86
好的。你有准确的错误信息吗?很感激您不能分享所有代码。但是,您能否设置一个非常基本的解决方案来复制错误并分享?@user1207289 - Danhol86

0

我能够通过从当前 AppDomain 加载程序集来使这个在我的项目中工作。

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    catalog.Catalogs.Add(new AssemblyCatalog(assembly));
}

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