如何 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。