【问题标题】:.NET Core Assembly.LoadFile at PostBuild eventPostBuild 事件中的 .NET Core Assembly.LoadFile
【发布时间】:2018-06-26 17:58:59
【问题描述】:

我需要在构建后从我的一些 C# 类生成 typescript 文件。

我创建了dotnet cli tool 并添加了构建后事件

dotnet tsgenerator "$(TargetPath)"

其中$(TargetPath) 是指向的宏,例如D:\Test\bin\Release\netcoreapp2.0\my.dll

接下来,我尝试加载程序集:

public static void Main(string[] args)
{
    var dllPath = args[0]; // "D:\Test\bin\Release\netcoreapp2.0\my.dll"
    var assembly = Assembly.LoadFile(dllPath);
    var types = assembly.GetExportedTypes(); // Throws exception
}

但是对于某些引用程序集(例如,Microsoft.AspNetCore.Antiforgery),我得到了 ReflectionTypeLoadException,上面写着 Could not load file or assembly

如何为 .NET Core 应用程序加载程序集?

【问题讨论】:

  • 什么是dllPath
  • @DanielA.White 来自 MSBuild 的参数 $(TargetPath)
  • 这是怎么设置的?
  • @DanielA.White 项目设置 -> 构建事件 -> 构建后事件:dotnet tsgenerator "$(TargetPath)"
  • 在c#中......

标签: c# .net asp.net-core .net-core


【解决方案1】:

我在github issue 找到了解决方案。 amits1995angelcalvasp发消息。

我已将 <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> 添加到我的 csproj 并使用此代码加载程序集:

public static class AssemblyLoader
{
    public static Assembly LoadFromAssemblyPath(string assemblyFullPath)
    {
        var fileNameWithOutExtension = Path.GetFileNameWithoutExtension(assemblyFullPath);
        var fileName = Path.GetFileName(assemblyFullPath);
        var directory = Path.GetDirectoryName(assemblyFullPath);

        var inCompileLibraries = DependencyContext.Default.CompileLibraries.Any(l => l.Name.Equals(fileNameWithOutExtension, StringComparison.OrdinalIgnoreCase));
        var inRuntimeLibraries = DependencyContext.Default.RuntimeLibraries.Any(l => l.Name.Equals(fileNameWithOutExtension, StringComparison.OrdinalIgnoreCase));

        var assembly = (inCompileLibraries || inRuntimeLibraries)
            ? Assembly.Load(new AssemblyName(fileNameWithOutExtension))
            : AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFullPath);

        if (assembly != null)
            LoadReferencedAssemblies(assembly, fileName, directory);

        return assembly;
    }

    private static void LoadReferencedAssemblies(Assembly assembly, string fileName, string directory)
    {
        var filesInDirectory = Directory.GetFiles(directory).Where(x => x != fileName).Select(x => Path.GetFileNameWithoutExtension(x)).ToList();
        var references = assembly.GetReferencedAssemblies();

        foreach (var reference in references)
        {
            if (filesInDirectory.Contains(reference.Name))
            {
                var loadFileName = reference.Name + ".dll";
                var path = Path.Combine(directory, loadFileName);
                var loadedAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
                if (loadedAssembly != null)
                    LoadReferencedAssemblies(loadedAssembly, loadFileName, directory);
            }
        }
    }
}

用法:

public static void Main(string[] args)
{
    var dllPath = args[0]; // "D:\Test\bin\Release\netcoreapp2.0\my.dll"
    var assembly = AssemblyLoader.LoadFromAssemblyPath(dllPath);
    var types = assembly.GetExportedTypes(); // No exceptions
}

【讨论】:

  • 我的答案是一样的!
  • @Prateek 在我的情况下,您的解决方案将System.InvalidOperationException: 'Sequence contains no elements' 抛出var assembly = Assembly.Load(new AssemblyName(res.First().Name));var deped = asl.CallForDependency(asm.GetName()); 调用。来自 github issue 的代码没有异常并且工作正常。
【解决方案2】:

好吧,您可以加载程序集,但 GetTypes()GetExportedTypes() 依赖于该程序集中的公共类,如果它们有外部引用,则会出现此异常。

答案: 这意味着该程序集的Types 依赖于当前.NetCore 在运行时无法访问的其他程序集,因为它无法连接到其他依赖程序集

解决方案:
获取 DLL 程序集的依赖关系并编译所有程序集,然后迭代加载每个程序集以获取所有 ExportedTypes(即公开可见的类型)

代码:

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;// add this nuget

class Program
{
    static void Main(string[] args)
    {
        var asl = new AssemblyLoader();
        var asm = asl.LoadFromAssemblyPath(@"C:\temp\Microsoft.AspNetCore.Antiforgery.dll");
        try
        {

            var y = asm.GetExportedTypes();
            Console.WriteLine(y);
        }
        catch (Exception e1)
        {
            Console.WriteLine("Got exception at first attempt of GetExportedTypes ");
            Console.WriteLine("\t*********" + e1.Message + "**************");

            var deped = asl.CallForDependency(asm.GetName());

            try
            {
                Console.WriteLine("\n" + deped.ToString());
                Console.WriteLine("----------All Exported Types------------");

                foreach (var item in deped.ExportedTypes)
                {
                    Console.WriteLine(item);
                }
            }
            catch (Exception e2)
            {

                Console.WriteLine("Got exception at second attempt of GetExportedTypes ");
                Console.WriteLine("\t*********" + e2.Message + "**************");
            }

        }
        Console.ReadLine();

    }
}

public class AssemblyLoader :AssemblyLoadContext
{
    protected override Assembly Load(AssemblyName assemblyName)
    {
        var deps = DependencyContext.Default;
        var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
        var assembly = Assembly.Load(new AssemblyName(res.First().Name));
        return assembly;
    }

    public Assembly CallForDependency(AssemblyName assemblyName)
    {
        return this.Load(assemblyName);
    }
}

输出:

Got exception at first attempt of GetExportedTypes
        *********Could not load file or assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. An operation is not legal in the current state. (Exception from HRESULT: 0x80131509)**************

Microsoft.AspNetCore.Antiforgery, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
----------All Exported Types------------
Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions
Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException
Microsoft.AspNetCore.Antiforgery.IAntiforgery
Microsoft.AspNetCore.Antiforgery.IAntiforgeryAdditionalDataProvider
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryFeature
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryOptionsSetup
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgerySerializationContext
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgerySerializationContextPooledObjectPolicy
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryToken
Microsoft.AspNetCore.Antiforgery.Internal.BinaryBlob
Microsoft.AspNetCore.Antiforgery.Internal.CryptographyAlgorithms
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryAdditionalDataProvider
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenGenerator
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenSerializer
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenStore
Microsoft.AspNetCore.Antiforgery.Internal.DefaultClaimUidExtractor
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryFeature
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryTokenGenerator
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryTokenSerializer
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryTokenStore
Microsoft.AspNetCore.Antiforgery.Internal.IClaimUidExtractor

ReflectionTypeLoadException的参考和解释: Assembly.GetTypes Method ()

ReflectionTypeLoadException

程序集包含一种或多种无法加载的类型。这 此异常的Types 属性返回的数组包含一个类型 加载的每种类型的对象和可以加载的每种类型的 null 未加载,而 LoaderExceptions 属性包含 无法加载的每种类型的异常。

备注

返回的数组包含嵌套类型。

如果在程序集上调用 GetTypes 方法并且其中的类型 程序集依赖于程序集中尚未被 加载(例如,如果它派生自第二个中的类型 程序集),一个ReflectionTypeLoadException 被抛出。例如,这个 如果第一个程序集加载了 ReflectionOnlyLoadReflectionOnlyLoadFrom 方法,第二个 程序集未加载。加载程序集也可能发生这种情况 如果第二个程序集无法加载,则使用 Load 和 LoadFile 方法 在调用GetTypes 方法时定位。

注意

如果一个类型已被转发到另一个程序集,则不包括在内 在返回的数组中。有关类型转发的信息,请参阅类型 在Common Language Runtime转发。

链接:

【讨论】:

    【解决方案3】:

    尝试在程序集中加载的 LoadFrom 方法,而不是 LoadFile:

    public static void Main(string[] args)
    {
        var dllPath = args[0]; // "D:\Test\bin\Release\netcoreapp2.0\my.dll"
        var assembly = Assembly.LoadFrom(dllPath);
        var types = assembly.GetExportedTypes(); // Throws exception
    }
    

    您还需要将 ddl 文件中的相同引用添加到当前项目中,以便定义类型。

    【讨论】:

    • 这是一个带有 nuget 依赖的 MVC 项目。 Assembly.LoadFrom 给出相同的错误:Could not load file or assembly 'Microsoft.AspNetCore.Antiforgery, Version=2.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. Could not find file specified. 并且所有通过 nuget 程序集引用的许多类似错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 2018-09-05
    • 1970-01-01
    相关资源
    最近更新 更多