重新编译组件以分开净5的应用程序

发布于 2025-01-24 18:33:52 字数 3537 浏览 2 评论 0 原文

我有一个Net 5.0控制台应用程序,我试图从中编译和执行外部代码,但也能够更新代码,卸载先前创建的AppDomain并重新编译所有内容。

可以处理代码编译并加载

using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using System.Runtime.Loader;

namespace Scripting
{
    public static class ScriptCompiler
    {
        public static Dictionary<string, AppDomain> _appDomainDict = new();
        
        public static object CompileScript(string scriptpath)
        {
            var tree = SyntaxFactory.ParseSyntaxTree(File.ReadAllText(scriptpath));
            
            //Adding basic references
            List<PortableExecutableReference> refs = new List<PortableExecutableReference>();
            var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")));


            // A single, immutable invocation to the compiler
            // to produce a library
            string hash_name = scriptpath.GetHashCode();

            if (_appDomainDict.ContainsKey(hash_name))
            {
                AppDomain.Unload(_appDomainDict[hash_name]);
                _appDomainDict.Remove(hash_name);
            }
            
            AppDomain new_domain = AppDomain.CreateDomain(hash_name);
            _appDomainDict[hash_name] = new_domain;

            var compilation = CSharpCompilation.Create(hash_name)
              .WithOptions(
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
                                             optimizationLevel: OptimizationLevel.Release,
                                             allowUnsafe:true))
              .AddReferences(refs.ToArray())
              .AddSyntaxTrees(tree);
            MemoryStream ms = new MemoryStream();
            EmitResult compilationResult = compilation.Emit(ms);
            ms.Seek(0, SeekOrigin.Begin);
            if (compilationResult.Success)
            {
                // Load the assembly
                Assembly asm = new_domain.Load(ms.ToArray());
                
                object main_ob = asm.CreateInstance("SomeClass");
                ms.Close();
                return main_ob;
            }
            else
            {
                foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
                {
                    string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}," +
                        $" Location: { codeIssue.Location.GetLineSpan()}," +
                        $" Severity: { codeIssue.Severity}";
                    Callbacks.Logger.Log(typeof(NbScriptCompiler), issue, LogVerbosityLevel.WARNING);
                }
                return null;
            }
        }

    }
}

这是我的整个静态类,当我尝试将汇编加载到当前域中并从实例化对象执行时, 它的所有好处。这种情况的问题在于,由于我想对代码进行频繁更新,即使我确保汇编名称不同。我最终将大量未使用的组件加载到当前域。

这就是为什么我一直在尝试创建一个新的域并在那里加载汇编的原因。但是由于某种原因,我得到一个平台不支持例外。这是在净5中无法做到的吗?有没有解决方法,还是我在这里做错了什么。

I have a NET 5.0 console application, from which I am trying to compile and execute external code BUT also be able to update the code, unload the previously created appdomain and re-compile everything.

This is my entire static class that handles code compilation and assembly loading

using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using System.Runtime.Loader;

namespace Scripting
{
    public static class ScriptCompiler
    {
        public static Dictionary<string, AppDomain> _appDomainDict = new();
        
        public static object CompileScript(string scriptpath)
        {
            var tree = SyntaxFactory.ParseSyntaxTree(File.ReadAllText(scriptpath));
            
            //Adding basic references
            List<PortableExecutableReference> refs = new List<PortableExecutableReference>();
            var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")));
            refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")));


            // A single, immutable invocation to the compiler
            // to produce a library
            string hash_name = scriptpath.GetHashCode();

            if (_appDomainDict.ContainsKey(hash_name))
            {
                AppDomain.Unload(_appDomainDict[hash_name]);
                _appDomainDict.Remove(hash_name);
            }
            
            AppDomain new_domain = AppDomain.CreateDomain(hash_name);
            _appDomainDict[hash_name] = new_domain;

            var compilation = CSharpCompilation.Create(hash_name)
              .WithOptions(
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
                                             optimizationLevel: OptimizationLevel.Release,
                                             allowUnsafe:true))
              .AddReferences(refs.ToArray())
              .AddSyntaxTrees(tree);
            MemoryStream ms = new MemoryStream();
            EmitResult compilationResult = compilation.Emit(ms);
            ms.Seek(0, SeekOrigin.Begin);
            if (compilationResult.Success)
            {
                // Load the assembly
                Assembly asm = new_domain.Load(ms.ToArray());
                
                object main_ob = asm.CreateInstance("SomeClass");
                ms.Close();
                return main_ob;
            }
            else
            {
                foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
                {
                    string issue = 
quot;ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}," +
                        
quot; Location: { codeIssue.Location.GetLineSpan()}," +
                        
quot; Severity: { codeIssue.Severity}";
                    Callbacks.Logger.Log(typeof(NbScriptCompiler), issue, LogVerbosityLevel.WARNING);
                }
                return null;
            }
        }

    }
}

Its all good when I am trying load the assembly in the current domain and execute from the instantiated object. The problem with this case is that since I wanna do frequent updates to the code, even if I make sure that the assembly names are different. I'll end up loading a ton of unused assemblies to the current domain.

This is why I've been trying to create a new domain and load the assembly there. But for some reason I get a platform not supported exception. Is this not possible to do in NET 5? Are there any workarounds or am I doing something wrong here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

拥醉 2025-01-31 18:33:52

好的,事实证明,AppDomain对Net Core +的支持非常有限,尤其似乎只有一个AppDomain

在.net核心上,AppDomain实施受设计的限制,
不提供隔离,卸载或安全边界。为了
.NET核心,正好有一个AppDomain。隔离和卸载是
通过汇编LoadContext提供。安全边界应该是
由过程边界和适当的远程技术提供。

来源:

的确,当尝试使用 assemblyloadContext 并通过这些上下文创建对象实例时,一切都像魅力一样工作呢

最后一个注意事项是,如果创建上下文未标记为收藏品,则不可能将其卸载。但这可以很容易地在 assemblyLoadContext 构造期间设置。

Ok, it turns out that AppDomain support for NET Core + is very limited and in particular there seems to be only one appdomain

On .NET Core, the AppDomain implementation is limited by design and
does not provide isolation, unloading, or security boundaries. For
.NET Core, there is exactly one AppDomain. Isolation and unloading are
provided through AssemblyLoadContext. Security boundaries should be
provided by process boundaries and appropriate remoting techniques.

Source: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain?view=net-6.0

And indeed, when trying to use AssemblyLoadContext and create object instances through these contexts everything worked like a charm!

One last note is that if the created context is not marked as collectible, its not possible to unload it. But this can be very easily set during AssemblyLoadContext construction.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文