卸载AppDomain后,除一个程序集外,所有程序集都被释放

本文关键字:程序集 释放 一个 AppDomain 卸载 | 更新日期: 2023-09-27 18:11:23

所以,在做了大量关于如何沙箱c#脚本编译器的研究之后,被加载的程序集只加载到沙箱AppDomain而不是我的主AppDomain,我遇到了一个问题,我创建的所有dll都在卸载沙箱AppDomain时卸载了,除了一个。它来自一个脚本,看起来像:

return new Func<List<int>,int>
(
  (list) => 
  {
    var total = 0;
    foreach (int i in list) 
    {
      total += i;          
    }
    return total;
  }
);

现在脚本返回的结果是,它们最终都以字典的形式返回到主AppDomain。其余的脚本都返回简单的可序列化对象或原语,正如我所说,它们的所有程序集都正确卸载,并且我能够在主域仍然活跃的情况下删除它们。是否有可能,这个特定的返回必须"传递"回创建程序集到主AppDomain,因为它的值是一个函数?没有别的办法了吗?

我把沙箱放在首位的原因是,在一组脚本运行之后,我可以处置执行它们的对象,并且处置方法卸载沙箱域并删除所有创建的程序集。我希望能够在一个不断运行的环境中使用这个,比如一个web服务器,其中一个程序集的建立是有问题的,目前,每次一个脚本集运行一个函数的返回,我将有一个挥之不去的程序集。我不希望在使用这个库的文档旁边有一个星号,所以欢迎任何想法。

作为参考,下面是我编译脚本的代码:
var provider = new CSharpCodeProvider(new Dictionary<string, string>() { { CompilerOptionName, CompilerOptionVersion  } });
        var compilerParams = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = false };
        compilerParams.ReferencedAssemblies.AddRange(References);
        compilerParams.TempFiles = new TempFileCollection(BinPath);
        compilerParams.OutputAssembly = Path.Combine(BinPath,
            Utilities.AssemblyPrefix + ProfigurationExe.Profiguration.ID + "_" + Action.ID + "_Script" + Utilities.DllExt);
        // If no object is returned by user code, enter in a return null to satisfy returning an object for
        // default code template.  If they do have a return that will return first.
        Code = Code + ReturnNull;
        var parameterString = BuildParameterString();
        parameterString = !String.IsNullOrEmpty(parameterString) ? ", " + parameterString : String.Empty;
        // If someone simply imports namespaces separated by spaces, commas, or semicolons, create a proper using statement
        if (!String.IsNullOrEmpty(Imports) && !IsUsingRegex.IsMatch(Imports))
        {
            Imports = String.Join("; ", Imports.Split(" ,;".ToCharArray()).Select(s => Using + " " + s)) + "; ";
        }
        FinalCode = String.Format(Imports + CodeTemplate,
            new object[] {DefaultNamespace, ClassName, DefaultMethod, parameterString, Code});
        // This just is a variable of the code that will be compiled, with all spaces and line breaks removed
        var debugFriendlierFinalCode = U.CompressWhiteSpaceRegex.Replace(FinalCode.Replace("'r", String.Empty).Replace("'n", String.Empty), U.OneSpace);
        // Note that we are adding the import fields to the beginning in case user wants to import namespaces (and not have to always use fully qualified class names)
        var results = provider.CompileAssemblyFromSource(compilerParams, FinalCode);
        Assembly = results.CompiledAssembly;
        if (!results.Errors.HasErrors)
        {
            return Assembly;
        }

        // If there were compiler errors, aggregrate them into an exception.
        var errors = new StringBuilder("Dynamic Code Compiler Errors :'r'n");
        foreach (CompilerError error in results.Errors)
        {
            errors.AppendFormat("Line {0},{1}'t: {2}'n",
                error.Line, error.Column, error.ErrorText);
        }
        throw new ProfigurationException(errors.ToString());

卸载AppDomain后,除一个程序集外,所有程序集都被释放

Func是可序列化的,并且正在按您的意愿复制。但它指向驻留在要卸载的程序集中的代码。这就是为什么程序集也被加载到父AppDomain中。

不知道该推荐什么。我希望您不能卸载正在使用的代码。