Roslyn - 从内存中程序集创建元数据引用
Roslyn - Create MetadataReference from in-memory assembly
正在处理 ASP.NET 5 申请(Visual Studio 2015 CTP5)和 Microsoft.CodeAnalysis.CSharp。
如果我尝试创建一个程序集的 MetadataReference,该程序集是解决方案的一部分,将其作为对 CSharpCompilation.Create 的引用传递,我会得到 System.ArgumentException、"Empty path name is not legal"。
// Throws exception
MetadataReference.CreateFromAssembly(typeof(this).Assembly);
// Doesn't throw exception
MetadataReference.CreateFromAssembly(typeof(Object).Assembly);
如果我检查程序集的位置 属性,它是空的。我假设这与 ASP.NET 5 中在内存中编译应用程序的新方法有关,因此程序集不会存储在光盘上。
那么有没有一种方法可以为没有位置的程序集传递对 Roslyn 的引用 属性 或者目前是否不受支持?
编辑:
@JaredPar - @SLaks 已经突出显示了它失败的确切位置,但这里是完整的堆栈跟踪信息。在此之前,我正在从 System.* 程序集创建其他几个 MetadataReferences,其中任何一个都没有问题。
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()
你的问题不清楚。
如果内存中有已编译程序集的字节,请调用 MetadataReference.CreateFromImage()
。
如果要在同一工作区中添加对 Roslyn 项目的引用,请调用 Compilation.ToMetadataReference()
。
已经有一段时间了,但我确实在 github Roslyn 存储库上得到了答案,所以我会 post 它以防有人发现这个问题:
ASP.NET 5 对此有一个 API。你可以做 Razor 做的事 https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs#L132
这是在 Asp.Net 5 的 Beta1 左右,因此可能需要调整,但原理仍然相同 - 遵循 Asp.Net 本身通过 [=13 使用的 API =] 服务注入器将提供的。
感谢大卫·福勒
更新:此答案适用于 ASP.NET 5 Beta1。 API 发生了很大变化,在 Core 1.0 中,您可以从静态成员访问 AssemblyLoadContext 而不是使用 IAssemblyLoadContextAccessor:
System.Runtime.Loader.AssemblyLoadContext.Default
然后您可以调用 LoadFromStream 从二进制图像加载程序集。这是我使用的代码的粗略草图,删除了一些不相关的位:
// 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);
这不应该 运行 原样,而只是给出主要步骤的概念。
此外,从仅内存中的程序集生成元数据引用的问题也不再存在,因为这些在 Core 1.0 中不再存在,因此每个程序集都有一个位置 属性。所以获取这些引用基本上与 ASP.net 4:
中的过程相同
MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName(assemblyName)).Location);
从内存中程序集创建元数据引用:
var pi = assembly.GetType().GetMethod("GetRawBytes", BindingFlags.Instance | BindingFlags.NonPublic);
byte[] assemblyBytes = (byte[]) pi.Invoke(assembly, null);
MetadataReference.CreateFromImage(assemblyBytes)
- 从 AppDomain 中获取已经加载的程序集。
- 使用反射从内存中的程序集中获取字节[]。
- 创建元数据引用。
正在处理 ASP.NET 5 申请(Visual Studio 2015 CTP5)和 Microsoft.CodeAnalysis.CSharp。
如果我尝试创建一个程序集的 MetadataReference,该程序集是解决方案的一部分,将其作为对 CSharpCompilation.Create 的引用传递,我会得到 System.ArgumentException、"Empty path name is not legal"。
// Throws exception
MetadataReference.CreateFromAssembly(typeof(this).Assembly);
// Doesn't throw exception
MetadataReference.CreateFromAssembly(typeof(Object).Assembly);
如果我检查程序集的位置 属性,它是空的。我假设这与 ASP.NET 5 中在内存中编译应用程序的新方法有关,因此程序集不会存储在光盘上。
那么有没有一种方法可以为没有位置的程序集传递对 Roslyn 的引用 属性 或者目前是否不受支持?
编辑: @JaredPar - @SLaks 已经突出显示了它失败的确切位置,但这里是完整的堆栈跟踪信息。在此之前,我正在从 System.* 程序集创建其他几个 MetadataReferences,其中任何一个都没有问题。
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()
你的问题不清楚。
如果内存中有已编译程序集的字节,请调用 MetadataReference.CreateFromImage()
。
如果要在同一工作区中添加对 Roslyn 项目的引用,请调用 Compilation.ToMetadataReference()
。
已经有一段时间了,但我确实在 github Roslyn 存储库上得到了答案,所以我会 post 它以防有人发现这个问题:
ASP.NET 5 对此有一个 API。你可以做 Razor 做的事 https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs#L132
这是在 Asp.Net 5 的 Beta1 左右,因此可能需要调整,但原理仍然相同 - 遵循 Asp.Net 本身通过 [=13 使用的 API =] 服务注入器将提供的。
感谢大卫·福勒
更新:此答案适用于 ASP.NET 5 Beta1。 API 发生了很大变化,在 Core 1.0 中,您可以从静态成员访问 AssemblyLoadContext 而不是使用 IAssemblyLoadContextAccessor:
System.Runtime.Loader.AssemblyLoadContext.Default
然后您可以调用 LoadFromStream 从二进制图像加载程序集。这是我使用的代码的粗略草图,删除了一些不相关的位:
// 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);
这不应该 运行 原样,而只是给出主要步骤的概念。
此外,从仅内存中的程序集生成元数据引用的问题也不再存在,因为这些在 Core 1.0 中不再存在,因此每个程序集都有一个位置 属性。所以获取这些引用基本上与 ASP.net 4:
中的过程相同MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName(assemblyName)).Location);
从内存中程序集创建元数据引用:
var pi = assembly.GetType().GetMethod("GetRawBytes", BindingFlags.Instance | BindingFlags.NonPublic);
byte[] assemblyBytes = (byte[]) pi.Invoke(assembly, null);
MetadataReference.CreateFromImage(assemblyBytes)
- 从 AppDomain 中获取已经加载的程序集。
- 使用反射从内存中的程序集中获取字节[]。
- 创建元数据引用。