Roslyn - Create Metadata Using Internal Memory

Work on ASP.NET 5 (Visual Studio 2015 CTP5) and Microsoft.CodeAnalysis.CSharp.

If I try to create a MetadataReference for the assembly, which is part of the solution, to pass it as a reference to CSharpCompilation.Create, I get a System.ArgumentException: "An empty path is not legal."

// Throws exception MetadataReference.CreateFromAssembly(typeof(this).Assembly); // Doesn't throw exception MetadataReference.CreateFromAssembly(typeof(Object).Assembly); 

If I check the Location property of the assembly, it is empty. I assume this is due to a new way to compile applications in memory in ASP.NET 5, so that the assembly is not saved to disk.

So, is there a way to pass a reference to Roslyn for assembly without a location property, or is it currently not supported?

EDIT: @JaredPar - @SLaks accurately calculated where this happens, but here is the full stack trace for the info. I create several other metadata using systems. * Assemblies before this, and there are no problems with them.

 System.ArgumentException Empty path name is not legal. C:\Development\Incubator\net.framework\src\Webfuel.Services.Host\ScriptHelper\ScriptHelper.cs Line 86: Line 87: // Compile the code Line 88: var compilation = CSharpCompilation.Create( Line 89: assemblyName, Line 90: options: new CSharpCompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary), at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, Win32Native.SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) at System.IO.File.OpenRead(String path) at Microsoft.CodeAnalysis.InternalUtilities.FileStreamLightUp.OpenFileStream(String path) at Microsoft.CodeAnalysis.MetadataReference.CreateFromAssembly(Assembly assembly, MetadataReferenceProperties properties, DocumentationProvider documentation) at Microsoft.CodeAnalysis.MetadataReference.CreateFromAssembly(Assembly assembly) at Webfuel.Services.Host.ScriptHelper.CompileScriptImpl(String source) in C:\Development\Incubator\net.framework\src\Webfuel.Services.Host\ScriptHelper\ScriptHelper.cs:line 88 at Webfuel.Services.Host.ScriptHelper.<>c__DisplayClass0.<CompileTemplate>b__3(String source) in C:\Development\Incubator\net.framework\src\Webfuel.Services.Host\ScriptHelper\ScriptHelper.cs:line 71 at System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>.GetOrAdd(TKey key, Func<TKey, TValue> valueFactory) at Webfuel.Services.Host.ScriptHelper.CompileTemplate(String template) in C:\Development\Incubator\net.framework\src\Webfuel.Services.Host\ScriptHelper\ScriptHelper.cs:line 69 at Webfuel.Services.Host.SandboxContext.<ExecuteTemplateAsync>d__1.MoveNext() in C:\Development\Incubator\net.framework\src\Webfuel.Services.Host\SandboxContext.cs:line 176 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter<TResult>.GetResult() at Webfuel.Services.Host.SandboxHost.<ExecuteTemplateAsync>d__1.MoveNext() in C:\Development\Incubator\net.framework\src\Webfuel.Services.Host\SandboxHost.cs:line 39 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter<TResult>.GetResult() at Webfuel.Services.Sandbox.SandboxService.<ExecuteTemplateAsync>d__1.MoveNext() in C:\Development\Incubator\net.framework\src\Webfuel.Services.Sandbox\SandboxService.cs:line 47 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter<TResult>.GetResult() at Webfuel.Services.Server.ServerService.<ProcessContentRequestAsync>d__1.MoveNext() in C:\Development\Incubator\net.framework\src\Webfuel.Services.Server\ServerService.cs:line 179 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter<TResult>.GetResult() at Webfuel.Services.Server.ServerService.<ProcessRequestAsync>d__1.MoveNext() in C:\Development\Incubator\net.framework\src\Webfuel.Services.Server\ServerService.cs:line 73 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter<TResult>.GetResult() at Webfuel.App.ServerMiddleware.<Invoke>d__1.MoveNext() in C:\Development\Incubator\net.framework\src\Webfuel.App\Startup.cs:line 89 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Microsoft.AspNet.RequestContainer.ContainerMiddleware.<Invoke>d__1.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Microsoft.AspNet.Loader.IIS.KlrHttpApplication.<ProcessRequestAsyncImpl>d__1.MoveNext() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Microsoft.AspNet.Loader.IIS.HttpApplicationBase.<InvokeProcessRequestAsyncImpl>d__1.MoveNext() 
+7
source share
3 answers

It has been a while, but I got the answer to this in the glyub Roslyn repository, so I will post it in case anyone finds this question:

ASP.NET 5 has an API for this. You can do what Razor does https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs#L132

This was again in Beta1 Asp.Net 5, so configuration might be required, but the principle is the same - follow the API that Asp.Net itself uses through IAssemblyLoadContextAccessor , which will provide the service injector.

Thanks to David Fowler

UPDATE: this answer was for ASP.NET 5 Beta1. The API has changed a lot, and in Core 1.0, instead of using IAssemblyLoadContextAccessor, you can access AssemblyLoadContext from a static member:

 System.Runtime.Loader.AssemblyLoadContext.Default 

You can then call LoadFromStream to load the assembly from the binary image. Here is a very rough sketch of the code that I use with some irrelevant bits hacked:

  // Give the assembly a unique name var assemblyName = "Gen" + Guid.NewGuid().ToString().Replace("-", "") + ".dll"; // Build the syntax tree var syntaxTree = CSharpSyntaxTree.ParseText(source); // Compile the code var compilation = CSharpCompilation.Create( assemblyName, options: new CSharpCompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary), syntaxTrees: new List<SyntaxTree> { syntaxTree }, references: GetMetadataReferences()); // Emit the image of this assembly byte[] image = null; using (var ms = new MemoryStream()) { var emitResult = compilation.Emit(ms); if (!emitResult.Success) { throw new InvalidOperationException(); } image = ms.ToArray(); } Assembly assembly = null; // NETCORE using (var stream = new MemoryStream(image)) assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(stream); 

This should not start as it is, but simply give an idea of ​​the basic steps.

Also, the problem of creating links to metadata from an assembly only in memory no longer exists, since they no longer exist in Core 1.0, so each assembly has a Location property. Therefore, getting these links is basically the same as in ASP.net 4:

 MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName(assemblyName)).Location); 
+3
source

Your question is not clear.

If you have bytes of the compiled assembly in memory, call MetadataReference.CreateFromImage() .

If you want to add a link to a Roslyn project in the same workspace, call Compilation.ToMetadataReference() .

0
source

To create a MetadataReference from an assembly in memory:

 var pi = assembly.GetType().GetMethod("GetRawBytes", BindingFlags.Instance | BindingFlags.NonPublic); byte[] assemblyBytes = (byte[]) pi.Invoke(assembly, null); MetadataReference.CreateFromImage(assemblyBytes) 
  1. Get an already downloaded assembly from AppDomain.
  2. Get byte [] from this assembly in memory using reflection.
  3. Create MetadataReference.
0
source

Source: https://habr.com/ru/post/982445/


All Articles