如何 delete/free .dll 程序集,用 Roslyn 发出
How to delete/free .dll assembly, emitted with Roslyn
我正在尝试创建一个小型 Web 应用程序,它将接受带有代码和变量的 POST 请求,在 ASP.NET 核心服务器上编译它并打印结果。我做了一些研究,似乎使用 Roslyn 是唯一的方法。阅读指南后,我想出了下面提供的代码,发现在调用 GenerateAssembly() 方法后,负责创建程序集并调用它,.NET Core 主机进程锁定 mylib.dll 文件,直到服务器重新启动。我尝试使用 File.Delete() 删除文件,但它引发了拒绝访问异常。有没有办法unlock/delete那个DLL?我的想法之一是创建名称为 GUID 的 DLL,但我认为这不是很好的解决方案。
public static object GenerateAssembly(string code, string[] param)
{
var tree = SyntaxFactory.ParseSyntaxTree(code);
string fileName = "mylib.dll"; //Guid.NewGuid().ToString() + ".dll";
// Detect the file location for the library that defines the
// object type
var systemRefLocation = typeof(object).GetTypeInfo().Assembly.Location;
// Create a reference to the library
var systemReference = MetadataReference.CreateFromFile(systemRefLocation);
var compilation = CSharpCompilation.Create(fileName)
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(systemReference)
.AddSyntaxTrees(tree);
path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
var compilationResult = compilation.Emit(path); // On 2nd call exception is raised on this line
Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
object result = asm.GetType("RoslynCore.Helper").GetMethod("Calculate").
Invoke(null, new object[] { param });
return result;
}
感谢 Reniuz 解决了它,刚刚更改
Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
至 Assembly asm = Assembly.Load(System.IO.File.ReadAllBytes(path));
死灵法术。
在 Full .NET Framework 中,您曾经为此使用 AppDomains。
但是,.NET Core 不再使用 AppDomains,但在 .NET Core 3 中,您现在有了 LoadContexts。
但是......如果你只是将 asselby 加载到默认上下文中,
System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
你得到:
System.InvalidOperationException: "Cannot unload non-collectible
AssemblyLoadContext."
所以你必须在不同的上下文中加载程序集(类似于 AppDomain)
public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
public CollectibleAssemblyLoadContext() : base(isCollectible: true)
{ }
protected override Assembly Load(AssemblyName assemblyName)
{
return null;
}
}
byte[] result = null; // Assembly Emit-result from roslyn
System.Runtime.Loader.AssemblyLoadContext context = new CollectibleAssemblyLoadContext();
System.IO.Stream ms = new System.IO.MemoryStream(result);
System.Reflection.Assembly assembly = context.LoadFromStream(ms);
System.Type programType = assembly.GetType("RsEval");
MyAbstractClass eval = (MyAbstractClass) System.Activator.CreateInstance(programType);
eval.LoadContext = context;
eval.Stream = ms;
// do something here with the dynamically created class "eval"
然后你可以说
eval.LoadContext.Unload();
eval.Stream.Dispose();
如果您将其放入抽象 class 的 IDisposable 接口中,那么如果您愿意,您可以只使用 using。
我正在尝试创建一个小型 Web 应用程序,它将接受带有代码和变量的 POST 请求,在 ASP.NET 核心服务器上编译它并打印结果。我做了一些研究,似乎使用 Roslyn 是唯一的方法。阅读指南后,我想出了下面提供的代码,发现在调用 GenerateAssembly() 方法后,负责创建程序集并调用它,.NET Core 主机进程锁定 mylib.dll 文件,直到服务器重新启动。我尝试使用 File.Delete() 删除文件,但它引发了拒绝访问异常。有没有办法unlock/delete那个DLL?我的想法之一是创建名称为 GUID 的 DLL,但我认为这不是很好的解决方案。
public static object GenerateAssembly(string code, string[] param)
{
var tree = SyntaxFactory.ParseSyntaxTree(code);
string fileName = "mylib.dll"; //Guid.NewGuid().ToString() + ".dll";
// Detect the file location for the library that defines the
// object type
var systemRefLocation = typeof(object).GetTypeInfo().Assembly.Location;
// Create a reference to the library
var systemReference = MetadataReference.CreateFromFile(systemRefLocation);
var compilation = CSharpCompilation.Create(fileName)
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(systemReference)
.AddSyntaxTrees(tree);
path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
var compilationResult = compilation.Emit(path); // On 2nd call exception is raised on this line
Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
object result = asm.GetType("RoslynCore.Helper").GetMethod("Calculate").
Invoke(null, new object[] { param });
return result;
}
感谢 Reniuz 解决了它,刚刚更改
Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
至 Assembly asm = Assembly.Load(System.IO.File.ReadAllBytes(path));
死灵法术。
在 Full .NET Framework 中,您曾经为此使用 AppDomains。
但是,.NET Core 不再使用 AppDomains,但在 .NET Core 3 中,您现在有了 LoadContexts。
但是......如果你只是将 asselby 加载到默认上下文中,
System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
你得到:
System.InvalidOperationException: "Cannot unload non-collectible AssemblyLoadContext."
所以你必须在不同的上下文中加载程序集(类似于 AppDomain)
public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
public CollectibleAssemblyLoadContext() : base(isCollectible: true)
{ }
protected override Assembly Load(AssemblyName assemblyName)
{
return null;
}
}
byte[] result = null; // Assembly Emit-result from roslyn
System.Runtime.Loader.AssemblyLoadContext context = new CollectibleAssemblyLoadContext();
System.IO.Stream ms = new System.IO.MemoryStream(result);
System.Reflection.Assembly assembly = context.LoadFromStream(ms);
System.Type programType = assembly.GetType("RsEval");
MyAbstractClass eval = (MyAbstractClass) System.Activator.CreateInstance(programType);
eval.LoadContext = context;
eval.Stream = ms;
// do something here with the dynamically created class "eval"
然后你可以说
eval.LoadContext.Unload();
eval.Stream.Dispose();
如果您将其放入抽象 class 的 IDisposable 接口中,那么如果您愿意,您可以只使用 using。