Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you add references to types compiled in a memory stream using Roslyn?

As input for compilation I have a string with the following code in it:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string EmailAddress { get; set; }
}

I have the following code, note the comment at [01]. The intent here is to take a string that contains a class (from this.Source and emit the code for the assembly into a MemoryStream.

var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
var assemblyName = Guid.NewGuid().ToString();
var syntaxTrees = CSharpSyntaxTree.ParseText(this.Source);

// build references up
var references = new List<MetadataReference>();
//[01] references.Add("System.dll")); 

// set up compilation
var compilation = CSharpCompilation.Create(assemblyName)
    .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
    .AddReferences(references)
    .AddSyntaxTrees(syntaxTrees);

// build the assembly
Assembly assembly;
using (var stream = new MemoryStream())
{
    // this is broked...
    EmitResult compileResult = compilation.Emit(stream);
    // [02] we get here, with diagnostic errors (check compileResult.Diagnostics)
    assembly = Assembly.Load(stream.GetBuffer());
}

At [02] I am getting a BadImageFormat exception, with the following in compileResult.Diagnostics:

[0]: warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options.
[1]: (2,18): error CS0518: Predefined type 'System.Object' is not defined or imported
[2]: (4,16): error CS0518: Predefined type 'System.String' is not defined or imported
[3]: (4,40): error CS0518: Predefined type 'System.Void' is not defined or imported
[4]: (5,16): error CS0518: Predefined type 'System.String' is not defined or imported
[5]: (5,39): error CS0518: Predefined type 'System.Void' is not defined or imported
[6]: (6,16): error CS0518: Predefined type 'System.Int32' is not defined or imported
[7]: (6,31): error CS0518: Predefined type 'System.Void' is not defined or imported
[8]: (7,16): error CS0518: Predefined type 'System.String' is not defined or imported
[9]: (7,43): error CS0518: Predefined type 'System.Void' is not defined or imported
[10]: (2,18): error CS1729: 'object' does not contain a constructor that takes 0 arguments

If I add a using System; to the start of the code I get the following error:

error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)

That leads me to believe that I don't have access to System from where I sit, which is why I stubbed in that pseudo-code references.Add("System.dll"));. It's obviously incorrect, but it's my intent.

For a little further information, the intent here is to then create an instance of the generated type dynamically and assign values to the properties.

What is the correct way to make references available to the compiler?

Or, is there another approach for compiling a class like this (from a string input)?

like image 414
MisterJames Avatar asked Nov 08 '14 22:11

MisterJames


2 Answers

Call the AssemblyMetadata.CreateFromFile(path) method, and pass typeof(object).Assembly.Location (or other assemblies).

like image 148
SLaks Avatar answered Nov 18 '22 12:11

SLaks


For .net core you can use

   public static class TrustedPlatformAssembly
   {
      public static string From( string shortDllName )
      {
         string dllString = AppContext.GetData( "TRUSTED_PLATFORM_ASSEMBLIES" ).ToString();
         var dlls = dllString.Split( ";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
         string dll = dlls.Single( d => d.Contains( shortDllName, StringComparison.OrdinalIgnoreCase ) );
         return dll;
      }
   }

I used it like this:

  public IEnumerable<string> GetDllsToReference()
  {
     // add a big pile of .net dlls, all using AppContext.GetData( "TRUSTED_PLATFORM_ASSEMBLIES" )
     List<string> dllsToReturn = new List<string>()
     {
        "mscorlib.dll",
        "system.runtime.dll",
        "system.private.corelib.dll",
        "netstandard.dll",
        "System.Text.RegularExpressions.dll",
        "system.linq.dll",
        "system.net.dll",
        "system.data.dll",
        "System.Collections.dll",
        "System.IO.dll",
        "System.IO.FileSystem.dll",
        "System.Runtime.Extensions.dll",
        "System.Windows.Forms.dll",
        "UIAutomationClient.dll",
        "UIAutomationTypes.dll",
        "Accessibility.dll",
        "System.Configuration.ConfigurationManager.dll",
        "PresentationFramework.dll",
        "PresentationCore.dll",
        "System.CodeDom.dll",
        "System.Drawing.Common.dll",
        "System.Windows.Controls.Ribbon.dll",
        "WindowsFormsIntegration.dll",
        "System.Xaml.dll",
        "System.IO.Packaging.dll",
        "System.Xml.XmlSerializer.dll",
        "System.Xml.XDocument.dll",
        "System.Xml.ReaderWriter.dll",
        "System.Private.Xml.dll",
        "System.Private.Xml.Linq",
        "System.Private.Uri.dll",
        "System.Diagnostics.Process.dll",
        "System.Drawing.Primitives.dll",
        "System.Data.Common.dll",
        "System.ComponentModel.Primitives.dll",
        "Microsoft.VisualBasic.Core.dll",
        "System.ComponentModel.TypeConverter.dll",
        "System.Linq.Expressions.dll",
        "System.ObjectModel.dll",
        "System.Diagnostics.Tools.dll",
        "System.Net.Http.dll",
        "System.ComponentModel.dll",
        "Microsoft.Win32.Primitives.dll",
        "System.Collections.Specialized.dll",
        "System.Collections.NonGeneric.dll",
        "System.Diagnostics.FileVersionInfo.dll",
        "System.Resources.ResourceManager.dll",
        "System.Net.Requests.dll",
        "System.Net.Mail.dll",
        "System.Linq.Parallel.dll",
        "System.Net.Primitives.dll",
        "System.Resources.Writer.dll",
        "System.Collections.Concurrent.dll",
        "System.Collections.Immutable.dll",
        "System.Security.Claims.dll",
        "System.Console.dll",
        "System.Web.HttpUtility.dll",
        "System.Runtime.InteropServices.dll",
        "System.Threading.Tasks.Parallel.dll",
        "WindowsBase.dll",
        "Microsoft.Win32.Registry.dll",
        "System.Security.Principal.Windows.dll",
     };

     foreach ( var dll in dllsToReturn )
     {
        yield return TrustedPlatformAssembly.From( dll );
     }
  }
like image 3
Derek Avatar answered Nov 18 '22 11:11

Derek