【问题标题】:How to delete/free .dll assembly, emitted with Roslyn如何删除/释放 .dll 程序集,由 Roslyn 发出
【发布时间】:2018-07-26 14:30:06
【问题描述】:

我正在尝试创建一个小型 Web 应用程序,它将接受带有代码和变量的 POST 请求,在 ASP.NET Core 服务器上编译它并打印结果。我做了一些研究,似乎使用 Roslyn 是唯一的方法。阅读指南后,我想出了下面提供的代码,发现在调用 GenerateAssembly() 方法后,负责创建程序集并调用它,.NET Core Host 进程锁定 mylib.dll 文件,直到服务器重新启动。我尝试使用 File.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;
    }

【问题讨论】:

  • 很抱歉,但带有 guid 文件名的想法很糟糕 :) 您将程序集加载到 appdomain,因此文件被锁定。因此,您需要加载到单独的 appdomain 然后销毁它,或者您可以尝试以不同的方式将程序集加载到当前 appdomain,例如 this answer
  • 对 guid 的期望值也很高。非常感谢您的链接,这真的解决了它!
  • 您应该将生成的程序集专门加载到另一个应用程序域中,这是您再次释放内存的唯一方法。也许 roslyn 可以将程序集直接发送到内存流,然后您甚至不必在加载之前将其写入磁盘。

标签: c# asp.net dll roslyn


【解决方案1】:

感谢 Reniuz 解决了这个问题,只是改变了 Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);Assembly asm = Assembly.Load(System.IO.File.ReadAllBytes(path));

【讨论】:

    【解决方案2】:

    亡灵术。
    在完整的 .NET Framework 中,您曾经为此使用 AppDomains。
    但是,.NET Core 不再使用 AppDomain,但在 .NET Core 3 中,您现在有了 LoadContexts。

    但是...如果您只是将 assemlby 加载到默认上下文中,使用

    System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(ms);
    

    你得到:

    System.InvalidOperationException:“无法卸载非收藏品 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();
    

    如果你把它放到抽象类的 IDisposable 接口中,那么你可以使用 using,如果你愿意的话。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-02
      相关资源
      最近更新 更多