从子文件夹解析装配的正确方法 [英] Proper way to resolving assemblies from subfolders

查看:105
本文介绍了从子文件夹解析装配的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的应用程序文件夹的样子:

 应用程序:
+ App.exe
+ App.exe.config
应用程序/插件:
+ Plugin1(文件夹)
应用程序/插件/插件1:
+ Plugin1.dll
+ SomeDll.dll

因此主应用程序App.exe查找插件文件夹并将{PluginName} .dll加载到内存中以运行它。此插件通常使用自己的相关程序集,必须进行加载(例如SomeDll.dll)。看来有时会造成严重的麻烦。我收到一个例外,例如找不到依赖程序集的依赖程序集,我也不知道为什么。



例如,由于插件运行OwinSelfHost服务,我的插件必须加载许多其他dll。



加载示例:

  System.Web.Http.Owin 
Owin
Microsoft.Owin
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.Hosting

并且在加载时 Microsoft.Owin.Hosting ,然后引发无法加载的异常 Microsoft.Owin



异常如下:

 无法加载文件或程序集'Microsoft.Owin,Version = 2.0.2.0,Culture = neutral,PublicKeyToken = 31bf3856ad364e35'或它的依赖项之一。文件未找到。 


解决方案

我编写了此方法来解析程序集。



它基本上将 AssemblyResolve 事件挂接到当前应用程序域以检索从目录列表中请求程序集。



除了加载程序集文件并检查到以下位置,没有简单的方法来找到与命名空间匹配的程序集文件在哪里解析。



此外,它会丢弃一些不需要的程序集(例如序列化程序,资源...),并检测非.NET程序集的dll或exe。 / p>

更好的方法是使用全局程序集缓存,但我们希望插件可以完全移动。

 公共静态类AssemblyResolver 
{
内部静态void Hook(params string [ ]文件夹)
{
AppDomain.CurrentDomain.AssemblyResolve + =(sender,args)=>
{
//检查所请求的程序集是否是已加载程序集的一部分
varloadedAssembly = AppDomain.CurrentDomain.GetAssemblies()。FirstOrDefault(a => a.FullName == args。名称);
if(loadedAssembly!= null)
返回loadAssembly;

//当加载的控件尝试加载生成的XmlSerializer时,将调用此解析器-我们需要将其丢弃。
// http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization

var n =新的AssemblyName(args.Name);

if(n.Name.EndsWith(。xmlserializers,StringComparison.OrdinalIgnoreCase))
返回null;

// http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembly

if(n .Name.EndsWith(。resources,StringComparison.OrdinalIgnoreCase))
返回null;

字符串assy = null;

//找到相应的汇编文件
foreach(文件夹中的var dir)
{
assy = new [] { * .dll, *。 exe} .SelectMany(g => Directory.EnumerateFiles(dir,g))。FirstOrDefault(f =>
{
try {返回n.Name.Equals(AssemblyName.GetAssemblyName(f) .Name,StringComparison.OrdinalIgnoreCase);}
catch(BadImageFormatException){返回false; / *旁路程序集不是.net exe * /}
catch(Exception ex){抛出新的ApplicationException( Error加载程序集 + f,ex);}
});

if(assy!= null)
return Assembly.LoadFrom(assy);复制代码
}

抛出新的ApplicationException( Assembly + args.Name + not found);
};
}
}

这是它的工作原理:

  AssemblyResolver.Hook( \Plugins, \CommonReferences); 

每次需要解析某些程序集时,都会得到一个已加载到内存中的程序集,否则将在任何给定的文件夹中搜索。


Here is how my application folders looks like:

Application:
+ App.exe
+ App.exe.config
Application/Plugins:
+ Plugin1 (folder)
Application/Plugins/Plugin1:
+ Plugin1.dll
+ SomeDll.dll

So main application App.exe looking for plugins folder and load {PluginName}.dll into memory to run it. This plugin usually uses it's own dependant assemblies which must be loaded (like SomeDll.dll). It appears that it make serious troubles sometimes. I receive exception that for example dependant assembly of dependant assembly cannot be found and I don't know why.

For example, My plugin must load lots of additional dlls becouse plugin runs OwinSelfHost service.

So it must load for example:

System.Web.Http.Owin
Owin
Microsoft.Owin
Microsoft.Owin.Host.HttpListener
Microsoft.Owin.Hosting

and when load Microsoft.Owin.Hosting then throw exception that cannot load Microsoft.Owin

Exception looks like:

Could not load file or assembly 'Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of it's dependencies. File not found.

解决方案

I wrote this method to resolve assemblies. It is tweaked to fit my needs.

It basically hooks a AssemblyResolve event to the current application domain to retrieve an requested assembly from a list of directories.

There is no easy way to find where the assembly file that match the namespace to resolve, except by loading an assembly file and check to which namespace it belongs to.

Plus, it discards some unwanted assemblies (like serializers, resources...) and detects dlls or exes that are not .NET assemblies.

A better approach would consist in using the Global Assembly Cache, but we want our plugins to be fully moveable. So here it is.

public static class AssemblyResolver
{
    internal static void Hook(params string[] folders)
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                // Check if the requested assembly is part of the loaded assemblies
                var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
                if (loadedAssembly != null)
                    return loadedAssembly;

                // This resolver is called when an loaded control tries to load a generated XmlSerializer - We need to discard it.
                // http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization

                var n = new AssemblyName(args.Name);

                if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase))
                    return null;

                // http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl

                if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
                    return null;

                string assy = null;

                // Find the corresponding assembly file
                foreach (var dir in folders)
                {
                    assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(dir, g)).FirstOrDefault(f =>
                               {
                                   try { return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name, StringComparison.OrdinalIgnoreCase); }
                                   catch (BadImageFormatException) { return false; /* Bypass assembly is not a .net exe */ }
                                   catch (Exception ex) { throw new ApplicationException("Error loading assembly " + f, ex); }
                               });

                    if (assy != null)
                        return Assembly.LoadFrom(assy);
                }

                throw new ApplicationException("Assembly " + args.Name + " not found");
            };
    }
}

Here is how it works:

AssemblyResolver.Hook("\Plugins", "\CommonReferences");

Everytime some assemblies needs to be resolved, it will get the one that is loaded in memory, otherwise it will search in any given folders.

这篇关于从子文件夹解析装配的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆