2

I have a project that uses some dynamically compiled code and I am upgrading from .net framework to .net core 3.1.

I can't get a simple test case to include newtonsoft.json.dll and get the error "Type or namespace name 'Newtonsoft' could not be found. I had a similar problem when I first tried add the library, but got past it by using the currently loaded assemblies (Can't include Newtonsoft JSON in CSharpCodeProvider script). With "Core" I don't get errors about a library, but it doesn't know the type, like it didn't get loaded.

I tried both using the project libraries (commented out) and specifying them directly, but have the same issue. To recreate, make a new .netCore 3.1 console application called "TestScript" and install nuget packages "Microsoft.CodeAnalysis.CSharp.Scripting" v 3.7.0, "Newtonsoft.Json" v12.0.3 and use the following code.

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Diagnostics;
using Newtonsoft.Json.Linq;

namespace TestScript
{  
  class Program
  {
    public static void Example1()
    {
      var assemblyName = "UserScript";
      var code = @"namespace UserScript
                { 
                  using System;
                  using System.IO;
                  using System.Collections.Generic;
                  using Newtonsoft.Json.Linq;
                  public class RunScript
                  {
                    private const int x = 99;
                    public int Eval()
                    {
                      JObject j = new JObject();
                      return x; 
                    }
                  }
                }";

      SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
      var references = new List<MetadataReference>();
      //Load project libraries
      //var assemblies = AppDomain.CurrentDomain
      //                .GetAssemblies()
      //                .Where(a => !a.IsDynamic)
      //                .Select(a => a.Location);
      //foreach (var item in assemblies)
      //{
      //  if (!item.Contains("xunit"))
      //    references.Add(MetadataReference.CreateFromFile(item));
      //}

      //or specify the libraries to load.

      var coreDir = Directory.GetParent(typeof(Enumerable).GetTypeInfo().Assembly.Location);
      var exeDir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
      references.Add(MetadataReference.CreateFromFile(typeof(Object).GetTypeInfo().Assembly.Location));
      references.Add(MetadataReference.CreateFromFile(typeof(Uri).GetTypeInfo().Assembly.Location));
      references.Add(MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll"));
      references.Add(MetadataReference.CreateFromFile(coreDir.FullName + Path.DirectorySeparatorChar + "System.Runtime.dll"));
      if (File.Exists(exeDir + "\\Newtonsoft.Json.dll"))
        references.Add(MetadataReference.CreateFromFile(exeDir + "\\Newtonsoft.Json.dll"));
      else
        throw new Exception("Missing newtonsoft DLL");


      CSharpCompilation compilation = CSharpCompilation.Create(
          assemblyName,
          new[] { syntaxTree },
          new MetadataReference[]
          {
        MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
          },
          new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

      using (var memoryStream = new MemoryStream())
      {
        var result = compilation.Emit(memoryStream);

        if (result.Success)
        {
          memoryStream.Seek(0, SeekOrigin.Begin);
          Assembly assembly = Assembly.Load(memoryStream.ToArray());

          Type testClassType = assembly.GetType("TestNamespace.TestClass");
          var addResult = (int)testClassType.GetMethod("Add").Invoke(null, new object[] { 3, 4 });
          Console.WriteLine(addResult);
        }
        else
        {
          Console.WriteLine("Failed to compile");
          for (var i = 0; i < result.Diagnostics.Length; i++)
          {
            Console.WriteLine(result.Diagnostics[i].ToString());
          }
        }
      }
    }
    static void Main(string[] args)
    {
      JObject j = null; //to make sure newtonsoft is included if loading current projects libraries

      Example1();
    }
  }
}

1 Answer 1

4

Your code should work fine if you don't forget to use the references list you've built up.

See test code on .NET Fiddle (link) - I used the AppDomain method there (one you commented out).

CSharpCompilation compilation = CSharpCompilation.Create(
    assemblyName,
    new[] { syntaxTree },
    references, //<-- you're missing this
    new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

Also, your invocations weren't actually calling the right Class and Method, which I guess you'll find out after you get past the compilation issues.

// Non-existent Type and Method...
Type testClassType = assembly.GetType("TestNamespace.TestClass");
var addResult = (int)testClassType.GetMethod("Add").Invoke(null, new object[] { 3, 4 });

Edit: Here's the full working code in case the Fiddle gets deleted:

public static void Main(string[] args)
{
    var j = new JObject(); // ensure assembly is available
    Example1();
}

public static void Example1()
{
    var assemblyName = "UserScript";
    var code = @"namespace UserScript { 
                  using Newtonsoft.Json;
                  public class RunScript {
                   public static string Eval() {
                    return JsonConvert.SerializeObject(new int[] {1,2,3,4});
                   }
                  }
                }";

    SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
    
    //Load project libraries
    var references = AppDomain.CurrentDomain
        .GetAssemblies()
        .Where(a => !a.IsDynamic)
        .Select(a => a.Location)
        .Where(s => !string.IsNullOrEmpty(s))
        .Where(s => !s.Contains("xunit"))
        .Select(s => MetadataReference.CreateFromFile(s))
        .ToList()
        ;

    CSharpCompilation compilation = CSharpCompilation.Create(
        assemblyName,
        new[] { syntaxTree },
        references, //<-- you're missing this
        new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

    using (var memoryStream = new MemoryStream())
    {
        var result = compilation.Emit(memoryStream);

        if (result.Success)
        {
            memoryStream.Seek(0, SeekOrigin.Begin);
            Assembly assembly = Assembly.Load(memoryStream.ToArray());

            var testClassType = assembly.GetType("UserScript.RunScript");
            var invokeResult = (string)testClassType.GetMethod("Eval").Invoke(null, null);
            Console.WriteLine(invokeResult);
        }
        else
        {
            Console.WriteLine("Failed to compile");
            foreach (var diag in result.Diagnostics)
                Console.WriteLine(diag);
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Well now I feel a bit embarrassed. I know I changed that at one point, but I must have undid it somehow. Thanks for the help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.