简体   繁体   English

C# Roslyn 编译 - 在动态编译的代码中使用 Nuget 包 - 编译但在运行时抛出异常(无法加载文件或程序集)

[英]C# Roslyn Compile - Use Nuget Package within dynamically compiled Code - compiles but throws an Exception at runtime (Could not load file or assembly)

I try to accomplish using a Nuget Package in dynamic compiled code using Roslyn Compiler.我尝试使用 Roslyn Compiler 在动态编译代码中使用 Nuget 包来完成。

I want to use a Nuget Package ( in my example https://www.nuget.org/packages/TinyCsvParser/ ) within my code.我想在我的代码中使用 Nuget 包(在我的示例中为https://www.nuget.org/packages/TinyCsvParser/ )。 So I downloaded the package and extracted it to a folder (C:\\Data\\packages\\tinycsvparser.2.6.1)所以我下载了这个包并解压到一个文件夹(C:\\Data\\packages\\tinycsvparser.2.6.1)

But I don't want it to be in my applications direct dependencies.但我不希望它出现在我的应用程序直接依赖项中。 So it is not referenced in the project itself and I don't want to copy it to the bin/Debug-Folder.所以它没有在项目本身中引用,我不想将它复制到 bin/Debug-Folder。

The Nuget-Package-DLL should be able to be anywhere on my harddisk. Nuget-Package-DLL 应该能够位于我硬盘上的任何位置。

The compilation runs without any errors.编译运行没有任何错误。 But on Runtime on the line method.Invoke(fooInstance, null) I get the following Exception:但是在运行时在线 method.Invoke(fooInstance, null) 我得到以下异常:

Could not load file or assembly 'TinyCsvParser, Version=2.6.1.0, Culture=neutral, PublicKeyToken=d7df35b038077099' or one of its dependencies. The system cannot find the file specified.

How can I tell the programm where it should look for the DLL?我如何告诉程序它应该在哪里寻找 DLL? I tried it with the following line我用以下行尝试过

Assembly nugetAssembly = Assembly.LoadFrom(pathToNugetDLL);

But that did not help.但这并没有帮助。

Thank you for any advice on how to resolve this issue.感谢您提供有关如何解决此问题的任何建议。

Here is my Code (Prototype):这是我的代码(原型):

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;

namespace Playground
{
   public static class Program
   {
      private const string pathToNugetDLL = @"C:\Data\packages\tinycsvparser.2.6.1\lib\net45\TinyCsvParser.dll";

      private const string firstClass =
@"
using TinyCsvParser;

namespace A
{
    public class MyClass
    {
        public int MyFunction()
         {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, ';');
            return 1;
         }
    }
}";

      public static void Main()
      {
         CSharpParseOptions parseOptions = new CSharpParseOptions(LanguageVersion.CSharp7, DocumentationMode.Parse, SourceCodeKind.Regular);
         SyntaxTree parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(firstClass, parseOptions);

         List<string> defaultNamespaces = new List<string>() { };

         //// Referenzen über Kommentare heraussuchen:
         List<MetadataReference> defaultReferences = CreateMetadataReferences();

         var encoding = Encoding.UTF8;

         var assemblyName = Path.GetRandomFileName();
         var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
         var sourceCodePath = "generated.cs";

         var buffer = encoding.GetBytes(firstClass);
         var sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);

         var syntaxTree = CSharpSyntaxTree.ParseText(
             sourceText,
             new CSharpParseOptions(),
             path: sourceCodePath);

         var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
         var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, sourceCodePath, encoding);


         CSharpCompilationOptions defaultCompilationOptions =
               new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                       .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Debug).WithPlatform(Platform.AnyCpu)
                       .WithUsings(defaultNamespaces);

         CSharpCompilation compilation = CSharpCompilation.Create(
             assemblyName,
             syntaxTrees: new[] { encoded },
             references: defaultReferences,

             options: defaultCompilationOptions
         );

         using (var assemblyStream = new MemoryStream())
         using (var symbolsStream = new MemoryStream())
         {
            var emitOptions = new EmitOptions(
                    debugInformationFormat: DebugInformationFormat.Pdb,
                    pdbFilePath: symbolsName);

            var embeddedTexts = new List<EmbeddedText> { EmbeddedText.FromSource(sourceCodePath, sourceText) };

            EmitResult result = compilation.Emit(
                peStream: assemblyStream,
                pdbStream: symbolsStream,
                embeddedTexts: embeddedTexts,
                options: emitOptions);


            if (result.Success)
            {
               Console.WriteLine("Complation succeeded!");
               try
               {
                  Assembly nugetAssembly = Assembly.LoadFrom(pathToNugetDLL);

                  var assembly = Assembly.Load(assemblyStream.ToArray(), symbolsStream.ToArray());
                  var type = assembly.GetType("A.MyClass");

                  MethodInfo method = type.GetMethod("MyFunction");

                  var fooInstance = Activator.CreateInstance(type);
                  method.Invoke(fooInstance, null);
                  
               }
               catch (Exception ex)
               {
                  int i = 0;
               }
            }
         }
      }
      private static List<MetadataReference> CreateMetadataReferences()
      {
         string defaultPath = typeof(object).Assembly.Location.Replace("mscorlib", "{0}");

         var metadatenReferences = new List<MetadataReference>()
            {
                MetadataReference.CreateFromFile(string.Format(defaultPath, "mscorlib")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Data")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Core")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.ComponentModel.DataAnnotations")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Xml")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "netstandard")),
            };

         string strExtraDll = pathToNugetDLL;
         metadatenReferences.Add(MetadataReference.CreateFromFile(strExtraDll));

         return metadatenReferences;
      }
   }
         
}

I could solve the problem by using the event Handler我可以通过使用事件处理程序来解决问题

AppDomain.CurrentDomain.AssemblyResolve

With it i could resolve the Dependency.有了它,我可以解决依赖关系。

Here the final result of the prototype code:这里是原型代码的最终结果:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Playground
{
   public static class Program
   {

      public static void Main()
      {
         var test = new Test();
         test.TestMethod();
      }
   }
   public class Test

   {

      private const string pathToNugetDLL = @"C:\Data\packages\tinycsvparser.2.6.1\lib\net45\TinyCsvParser.dll";

      private const string firstClass =
@"
using TinyCsvParser;

namespace A
{
    public class MyClass
    {
        public int MyFunction()
         {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, ';');
            return 1;
         }
    }
}";


      public void TestMethod()
      {
         CSharpParseOptions parseOptions = new CSharpParseOptions(LanguageVersion.CSharp7, DocumentationMode.Parse, SourceCodeKind.Regular);
         SyntaxTree parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(firstClass, parseOptions);

         List<string> defaultNamespaces = new List<string>() { };

         //// Referenzen über Kommentare heraussuchen:
         List<MetadataReference> defaultReferences = CreateMetadataReferences();

         var encoding = Encoding.UTF8;

         var assemblyName = Path.GetRandomFileName();
         var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
         var sourceCodePath = "generated.cs";

         var buffer = encoding.GetBytes(firstClass);
         var sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);

         var syntaxTree = CSharpSyntaxTree.ParseText(
             sourceText,
             new CSharpParseOptions(),
             path: sourceCodePath);

         var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
         var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, sourceCodePath, encoding);


         CSharpCompilationOptions defaultCompilationOptions =
               new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                       .WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Debug).WithPlatform(Platform.AnyCpu)
                       .WithUsings(defaultNamespaces);

         CSharpCompilation compilation = CSharpCompilation.Create(
             assemblyName,
             syntaxTrees: new[] { encoded },
             references: defaultReferences,

             options: defaultCompilationOptions
         );

         using (var assemblyStream = new MemoryStream())
         using (var symbolsStream = new MemoryStream())
         {
            var emitOptions = new EmitOptions(
                    debugInformationFormat: DebugInformationFormat.Pdb,
                    pdbFilePath: symbolsName);

            var embeddedTexts = new List<EmbeddedText> { EmbeddedText.FromSource(sourceCodePath, sourceText) };

            EmitResult result = compilation.Emit(
                peStream: assemblyStream,
                pdbStream: symbolsStream,
                embeddedTexts: embeddedTexts,
                options: emitOptions);


            if (result.Success)
            {
               Console.WriteLine("Complation succeeded!");
               try
               {
                  AppDomain.CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve;

                  Assembly nugetAssembly = Assembly.LoadFrom(pathToNugetDLL);

                  var assembly = Assembly.Load(assemblyStream.ToArray(), symbolsStream.ToArray());
                  var type = assembly.GetType("A.MyClass");

                  MethodInfo method = type.GetMethod("MyFunction");

                  var fooInstance = Activator.CreateInstance(type);
                  method.Invoke(fooInstance, null);

               }
               catch (Exception ex)
               {
                  int i = 0;
               }
               finally
               {
                  AppDomain.CurrentDomain.AssemblyResolve -= AppDomain_AssemblyResolve;
               }
            }
         }
      }


      private Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args)
      {
         string[] assemblyInfoSplitted = args.Name.Split(',');
         string strSearchedForAssemblyName = assemblyInfoSplitted[0];

         var fileInfo = new FileInfo(pathToNugetDLL);
         var strAssemblyName = Regex.Replace(fileInfo.Name, ".dll", "", RegexOptions.IgnoreCase);
         if (strSearchedForAssemblyName.ToLower() == strAssemblyName.ToLower())
         {
            //File.ReadAllBytes because DLL might be deleted afterwards in the filesystem
            return Assembly.Load(File.ReadAllBytes(pathToNugetDLL));
         }

         throw new Exception($"Could not resolve Assembly '{strSearchedForAssemblyName}'.");
      }

      private static List<MetadataReference> CreateMetadataReferences()
      {
         string defaultPath = typeof(object).Assembly.Location.Replace("mscorlib", "{0}");

         var metadatenReferences = new List<MetadataReference>()
            {
                MetadataReference.CreateFromFile(string.Format(defaultPath, "mscorlib")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Data")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Core")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.ComponentModel.DataAnnotations")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "System.Xml")),
                MetadataReference.CreateFromFile(string.Format(defaultPath, "netstandard")),
            };

         string strExtraDll = pathToNugetDLL;
         metadatenReferences.Add(MetadataReference.CreateFromFile(strExtraDll));

         return metadatenReferences;
      }
   }

}

You have 2 issues here:你在这里有两个问题:

  1. Compiling编译
  2. Running跑步

Try this:试试这个:

public class Test
{
    private const string firstClass =
@"
using TinyCsvParser;

namespace A
{
    public class MyClass
    {
        public int MyFunction()
         {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, ';');
            return 1;
         }
    }
}";


    public void TestMethod()
    {
        var parseOptions = new CSharpParseOptions(LanguageVersion.CSharp7, DocumentationMode.Parse, SourceCodeKind.Regular);
        var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(firstClass, parseOptions);

        var defaultReferences = CreateMetadataReferences();

        var encoding = Encoding.UTF8;

        var assemblyName = Path.GetRandomFileName();
        var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
        var sourceCodePath = "generated.cs";

        var buffer = encoding.GetBytes(firstClass);
        var sourceText = SourceText.From(buffer, buffer.Length, encoding, canBeEmbedded: true);

        var syntaxTree = CSharpSyntaxTree.ParseText(
            sourceText,
            new CSharpParseOptions(),
            path: sourceCodePath);

        var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
        var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, sourceCodePath, encoding);

        CSharpCompilationOptions defaultCompilationOptions =
              new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
                  .WithOverflowChecks(true)
                  .WithOptimizationLevel(OptimizationLevel.Debug)
                  .WithPlatform(Platform.AnyCpu);

        CSharpCompilation compilation = CSharpCompilation.Create(
            assemblyName,
            syntaxTrees: new[] { encoded },
            references: defaultReferences,

            options: defaultCompilationOptions
        );

        using var assemblyStream = new MemoryStream();
        using var symbolsStream = new MemoryStream();

        var emitOptions = new EmitOptions(
                debugInformationFormat: DebugInformationFormat.Pdb,
                pdbFilePath: symbolsName);

        var embeddedTexts = new List<EmbeddedText> { EmbeddedText.FromSource(sourceCodePath, sourceText) };

        EmitResult result = compilation.Emit(
            peStream: assemblyStream,
            pdbStream: symbolsStream,
            embeddedTexts: embeddedTexts,
            options: emitOptions);

        if (result.Success)
        {
            Console.WriteLine("Complation succeeded!");

            try
            {
                var assembly = Assembly.Load(assemblyStream.ToArray(), symbolsStream.ToArray());
                var type = assembly.GetType("A.MyClass");

                MethodInfo method = type.GetMethod("MyFunction");

                var fooInstance = Activator.CreateInstance(type);
                method.Invoke(fooInstance, null);

                Console.WriteLine("Invocation succeeded!");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Invocation failed!\n{ex}");
            }
        }
        else
        {
            Console.WriteLine("Complation failed!");

            foreach (var diagnostic in result.Diagnostics)
            {
                Console.WriteLine(diagnostic.ToString());
            }
        }
    }

    private static IEnumerable<MetadataReference> CreateMetadataReferences()
    {
        var tinyCsvParserAssembly = typeof(TinyCsvParser.CsvParserOptions).Assembly;

        var metadatenReferences = new Dictionary<string, MetadataReference>();

        GetReferenecedAssemblies(metadatenReferences, tinyCsvParserAssembly);

        return metadatenReferences.Values;

        static void GetReferenecedAssemblies(Dictionary<string, MetadataReference> references, Assembly assembly)
        {
            var assemblyName = assembly.FullName;

            if (!references.ContainsKey(assemblyName))
            {
                references.Add(assemblyName, MetadataReference.CreateFromFile(assembly.Location));

                foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
                {
                    GetReferenecedAssemblies(references, Assembly.Load(referencedAssembly));
                }
            }
        }
    }
}

暂无
暂无

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

相关问题 C# 无法加载文件或程序集:NuGet - C# Could not load file or assembly : NuGet 无法为 NET Standard 加载 NuGet 包的文件或程序集异常 NET 5/6 - could not load file or assembly exception NET 5/6 of NuGet package for NET Standard 来自 NuGet package 的运行时异常引用类型:'无法加载文件或程序集'Foo'。 该系统找不到指定的文件。' - Runtime exception referencing type from a NuGet package: 'Could not load file or assembly 'Foo'. The system cannot find the file specified.' NuGet package 因“无法加载文件或程序集”而中断 - NuGet package breaks with "Could not load file or assembly" nuget 包重定向后无法加载文件或程序集“System.Runtime.InteropServices.RuntimeInformation” - Could not load file or assembly 'System.Runtime.InteropServices.RuntimeInformation' after nuget package retargetting C# .NET“无法加载文件或程序集 System.Runtime” - C# .NET "Could not load file or assembly System.Runtime" Nuget DLL地狱:无法加载文件或程序集System.Runtime - Nuget DLL Hell: Could not load file or assembly System.Runtime 无法在 3 层 winforms C# 应用程序中加载文件或程序集异常 - Could not load file or assembly exception in 3 layer winforms C# application Elmah MySql Nuget包问题'无法加载文件或程序集' - Elmah MySql Nuget Package Issue 'Could not load file or assembly' Nuget包引用导致“无法加载文件或程序集”? - Nuget package references cause “Could not load file or assembly” ?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM