繁体   English   中英

替代Assembly.LoadFile、Assembly.LoadFrom 和Assembly.Load?

[英]Alternative for Assembly.LoadFile, Assembly.LoadFrom and Assembly.Load?

我有一个最小的、可重现的示例有两个问题,该示例具有三个面向 .NET Core 3.1 的项目。 但我也想以.NET Standard 2.0为目标。

该示例适用于需要在运行时加载程序集并使用提供的程序集的应用程序。

加载的程序集引用另一个项目,该项目反过来使用 NuGet 包。

代码

  • project Host 是一个没有项目引用和包引用的控制台应用程序。 它包含文件 Program.cs:
class Program
{
    static void Main(string[] args)
    {
        var featurePath = System.IO.Path.GetFullPath(@"..\..\..\..\Feature\bin\Debug\netcoreapp3.1\Feature.dll");
        var featureAssembly = System.Reflection.Assembly.LoadFile(featurePath);
        var featureType = featureAssembly.GetType("SomeFeature");
        var featureInstance = System.Activator.CreateInstance(featureType);
        featureType.InvokeMember("PrintText",
            System.Reflection.BindingFlags.InvokeMethod, null, featureInstance, new object[0]);
    }
}
  • project Feature 是一个类库,它具有..\\Subfeature\\Subfeature.csproj的 ProjectReference 并包含文件 SomeFeature.cs:
public class SomeFeature
{
    public void PrintText()
    {
        System.Console.WriteLine(new SomeSubfeature().GetText());
    }
}
  • project Subfeature 是一个类库,它具有Newtonsoft.Json的 PackageReference 并包含文件 SomeSubfeature.cs:
public class SomeSubfeature
{
    public string GetText()
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject("Some Text");
    }
}

解决的问题

  • 第一个解决的问题是因为使用的程序集引用了另一个项目和/或使用了包。 Assembly.LoadFrom仅加载请求的程序集。 未加载引用项目的程序集和包。 这会导致 FileNotFoundException,因为无法找到 Subfeature。

    我可以解决通过更换这个问题(1) Assembly.LoadFile(featurePath)Assembly.LoadFrom(featurePath)以便在相同目录中的其他需要的DLL也可以被加载。 并且 (2) 通过添加到 Feature.csproj <PropertyGroup><CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies></PropertyGroup>使仅在发布期间复制到同一目录的包 DLL 在构建期间也被复制。

  • 第二个解决的问题是应用程序在加载 DLL 时锁定它们。 这会阻止我在应用程序仍在运行时部署较新版本的 DLL。 这使得发布新版本变得很麻烦,比如说,应用程序是 IIS 托管应用程序的一部分。 .NET Core 不再支持加载 DLL 的卷影副本。

    我可以通过 (1) 将Assembly.LoadFile(featurePath) ) 替换为Assembly.Load(System.IO.File.ReadAllBytes(featurePath))来解决这个问题,以便从加载之前读取的字节加载所需的 DLL。 (2) 如果其目录中的 DLL 文件发生问题,则让文件监视程序重新加载应用程序。

最后的问题

第一个问题的解决方案与第二个问题的解决方案不兼容。 Assembly.LoadFrom解决了我的第一个问题。 Assembly.Load解决了我的第二个问题。 但是我还没有找到可以同时解决这两个问题的Assembly.LoadFile替代方案。

AssemblyResolve事件处理程序添加到AppDomain.CurrentDomain并像读取所有字节一样处理程序集加载。

        const string location = @"..\..\..\..\Dependency\bin\Debug\netcoreapp3.1\";
        static void Main(string[] args)
        {
            var domain = AppDomain.CurrentDomain;
            domain.AssemblyResolve += Domain_AssemblyResolve;
            var type = Type.GetType("Dependency.DependentClass,Dependency, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
            var obj = Activator.CreateInstance(type);
            Console.WriteLine(obj.ToString());     
        }

        private static Assembly Domain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            var dll = $"{args.Name.Split(",")[0]}.dll";
            var path = Path.Combine(location, dll);
            var asm = Assembly.Load(File.ReadAllBytes(path));
            return asm;
        }

旁注:您需要提供类型的全名,否则会引发错误。 比如: "Dependency.DependentClass"

依赖程序集有 2 个依赖项,一个是Newtonsoft.Json NuGet 包,另一个是另一个项目。 Bar类驻留在另一个项目中。

 public class DependentClass
    {
        public int MyProperty { get; set; }

        public string AnotherProperty { get; set; }

        public override string ToString()
        {
            return JsonConvert.SerializeObject(new Bar());
        }
    }

编辑:在downvote之后,我再次查找了上述解决方案,虽然它有效,但似乎更可能是. net Framework . net Framework是一种解决方案,而不是.net Core所以更多. net Core . net Core类的解决方案是使用AssemblyLoadContext

定义自定义程序集加载上下文。

public class CustomLoadContext : AssemblyLoadContext
    {
        private readonly AssemblyDependencyResolver resolver;

        public CustomLoadContext(string mainAssemblyToLoadPath) : base(isCollectible: true)
        {
            resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath);
        }


        protected override Assembly Load(AssemblyName name)
        {
            Console.WriteLine("Resolving : {0}",name.FullName);
            var assemblyPath = resolver.ResolveAssemblyToPath(name);
            if (assemblyPath != null)
            {
                return Assembly.Load(File.ReadAllBytes(assemblyPath));
            }

            return Assembly.Load(name);
        }
    }

加载您的程序集并让自定义加载器上下文加载它的依赖项。

        const string location = @"..\..\..\..\Dependency\bin\Debug\netcoreapp3.1\";

        static void Main(string[] args)
        {
            var fullPath = Path.GetFullPath(location + "Dependency.dll");
            var clx = new CustomLoadContext(fullPath); // initialize custom context
            var asm = clx.LoadFromStream(new MemoryStream(File.ReadAllBytes(fullPath))); // load your desired assembly
            var ins = asm.CreateInstance("Dependency.DependentClass");  
            Console.WriteLine(ins.ToString());
       
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM