首页 > 解决方案 > 通过更改默认 CLR 探测顺序从自定义位置加载程序集

问题描述

我有一个应用程序,它包含与应用程序在同一目录上的所有依赖项,因此一切正常。但是,我希望能够按需从自定义位置加载程序集。该位置在应用程序启动之前是已知的,但重要的一点是程序集应该从自定义位置加载,即使它们存在于应用程序基目录中。

以下是我尝试过的几件事:

方法一:将目录添加到私有探测路径

// This method does not work for assemblies outside the app directory. Also, it ignores the private path if the assembly is present in the app directory
        private static void ConfigureCustomAssemblyLoading1(string directory)
        {
            AppDomain.CurrentDomain.AppendPrivatePath(directory);
        }

方法 2:通过连接到程序集解析事件

// The AssemblyResolve event is only fired if the resolution fails, so the assembly will not be loaded from custom path if present in app directory
        private static void ConfigureCustomAssemblyLoading2(string directory)
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                var requestedAsm = args.Name.Split(',')[0];
                var files = Directory.GetFiles(directory);
                foreach (var file in files)
                {
                    var asm = Path.GetFileNameWithoutExtension(file);
                    if (asm.Equals(requestedAsm))
                        return Assembly.LoadFrom(file);
                }

                return null;
            };
        }

方法3:在config中使用codebase标签

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Calculator"/>
        <codeBase version="1.0.0.0"
                  href="file://C:\test\Calculator.dll"/>
      </dependentAssembly>
    </assemblyBinding>

这优先于本地应用程序基目录,但是,由于有数百个依赖程序集,这不是一个方便的解决方案。如果我可以只指定探测目录,它就会解决问题。

下面是一个示例程序来试试这个:

        private static void Main(string[] args)
        {
            const string customDirectory = @"C:\test"; // force load assembly from here
            //ConfigureCustomAssemblyLoading1(customDirectory);
            ConfigureCustomAssemblyLoading2(customDirectory);
            CalculateSum();
            Console.ReadLine();
        }

        private static void CalculateSum()
        {
            var sum = CustomMath.Sum(1, 1);
            Console.WriteLine($"In case you do not know, sum of 1 and 1 is {sum}");
        }

标签: c#.netclr.net-assembly

解决方案


正如评论中指出的那样,您不应该替换在构建期间静态链接的库。如果程序集没有强命名(未签名),这可能会起作用,但不能保证它在 .NET 世界中有效并且是不好的做法。代码签名不仅是确保代码不被操纵的一种方式,而且是为了确保构建是一致的,并且您不会意外混合来自不同构建的库。如果库与预期版本不匹配,则构建中的问题可能会导致未定义的行为。

如果需要动态加载库(这本身是完全有效的),请在共享程序集中使用具有公共接口层的反射。


推荐阅读