在.NET 核心中没有 AppDomain! 为什么?

微软选择不在.NET 核心中支持应用域是否有一个强有力的理由?

应用程序域在构建长时间运行的服务器应用程序时特别有用,我们可能希望更新服务器装载的程序集,这是一种优雅的方式,而不需要关闭服务器。

如果没有 AppDomains,我们将如何在长时间运行的服务器进程中替换程序集?

AppDomains 还为我们提供了一种隔离服务器代码不同部分的方法。类似地,定制的 websocket 服务器可以在主应用程序域中使用套接字代码,而我们的服务在辅助应用程序域中运行。

没有 AppDomain,上述场景就不可能实现。

我可以看到一个论点,可能会谈到使用云的虚拟机概念来处理程序集更改,而不必承担 AppDomains 的开销。但这是微软的想法或说法吗?或他们有一个具体的原因和替代上述情况?

42500 次浏览

The point of the .NETCore subset was to keep a .NET install small. And easy to port. Which is why you can, say, run a Silverlight app on both Windows and OSX and not wait very long when you visit the web page. Downloading and installing the complete runtime and framework takes a handful of seconds, give or take.

Keeping it small inevitably requires features to be cut. Remoting was very high on that list, it is quite expensive. Otherwise well hidden, but you can for example see that delegates no longer have a functional BeginInvoke() method. Which put AppDomain on the cut list as well, you can't run code in an app domain without remoting support. So this is entirely by design.

At one point, I heard that unloading assemblies would be enabled without using domains. I think that the System.Runtime.Loader.AssemblyLoadContext type in System.Runtime.Loader.dll is related to this work, but I don't see anything there that enables unloading yet.

I have heard in a community standup or some talk of Microsoft that the isolation feature of AppDomains are better handled by processes (and actually the common pattern in other platforms) and the unloading is indeed planned as a normal feature unrelated to AppDomains.

App Domains

Why was it discontinued? AppDomains require runtime support and are generally quite expensive. While still implemented by CoreCLR, it’s not available in .NET Native and we don’t plan on adding this capability there.

What should I use instead? AppDomains were used for different purposes. For code isolation, we recommend processes and/or containers. For dynamic loading of assemblies, we recommend the new AssemblyLoadContext class.

Source: Porting to .NET Core | .NET Blog

Update for .NET Standard 2 and .NET Core 2

In .NET Standard 2 the AppDomain class is in there. However, many parts of that API will throw a PlatformNotSupportedException for .NET Core.

The main reason it's still in there is for basic stuff like registering an unhandled exception handler which will work.

The .NET Standard FAQ has this explanation:

Is AppDomain part of .NET Standard?

The AppDomain type is part of .NET Standard. Not all platforms will support the creation of new app domains, for example, .NET Core will not, so the method AppDomain.CreateDomain while being available in .NET Standard might throw PlatformNotSupportedException.

The primary reason we expose this type in .NET Standard is because the usage is fairly high and typically not associated with creating new app domains but for interacting with the current app domain, such as registering an unhandled exception handler or asking for the application's base directory.

Apart from that, the other answer and other answers also nicely explain why the bulk of AppDomain was still cut (e.g. throws a not supported exception).

You don't need AppDomains anymore, you now have LoadContexts:

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"

and then you can say

eval.LoadContext.Unload();
eval.Stream.Dispose();

Bonus if you put that into the IDisposable interface of the abstract class, then you can just use using, if you want to.

Note:
This assumes a fixed abstract class in a common assembly

public abstract class MyAbstractClass
{


public virtual void foo()
{}
}

and a dynamically runtime-generated class ( using Roslyn), referencing the abstract class in the common assembly, which implements e.g.:

public class RsEval: MyAbstractClass
{


public override void foo()
{}
}