.NET Core
AppDomains
software architecture
application isolation
programming concepts

No AppDomains in .NET Core Why?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

AppDomain was an important isolation feature in the classic .NET Framework, so its absence in .NET Core surprised many developers. The short answer is that .NET Core simplified the runtime around process-based isolation and cross-platform design, and the old AppDomain model no longer fit those goals cleanly.

What AppDomains Used To Provide

In .NET Framework, AppDomains offered a lightweight isolation boundary inside one process. They were used for scenarios such as:

  • loading plugins with some isolation
  • unloading assemblies by unloading the whole AppDomain
  • controlling configuration or evidence for a separate application boundary

A simplified .NET Framework style example looked like this conceptually:

csharp
// Classic .NET Framework concept, not .NET Core
var domain = AppDomain.CreateDomain("PluginDomain");

That capability was useful, but it came with runtime complexity, remoting boundaries, and behaviors that were difficult to make consistent across a modern, cross-platform runtime.

Why .NET Core Dropped Them

The design of .NET Core focused on a smaller, faster, more portable runtime. AppDomains were tightly connected to older runtime features such as remoting and code-access security models that .NET Core intentionally did not carry forward.

There were several practical reasons:

  • process isolation is stronger and easier to reason about than in-process domain isolation
  • many AppDomain scenarios depended on legacy runtime mechanisms that were removed
  • cross-platform runtime simplification mattered more than preserving every compatibility feature
  • startup, deployment, and hosting models in .NET Core moved toward cleaner process and container boundaries

In other words, AppDomains solved some real problems, but they were not cheap to keep.

What Replaced the Main Use Cases

The most important replacement for dynamic assembly loading is AssemblyLoadContext.

csharp
1using System;
2using System.Reflection;
3using System.Runtime.Loader;
4
5class PluginLoadContext : AssemblyLoadContext
6{
7    public PluginLoadContext() : base(isCollectible: true) { }
8}
9
10var loadContext = new PluginLoadContext();
11Assembly assembly = loadContext.LoadFromAssemblyPath("/path/to/MyPlugin.dll");
12Console.WriteLine(assembly.FullName);

AssemblyLoadContext is not a full AppDomain replacement. It focuses mainly on assembly loading and unloading, not on recreating the entire old boundary model.

For stronger isolation, the recommended answer is usually a separate process.

csharp
1using System.Diagnostics;
2
3var process = Process.Start(new ProcessStartInfo
4{
5    FileName = "dotnet",
6    Arguments = "MyWorker.dll",
7    UseShellExecute = false
8});

That gives you operating-system-level isolation, clearer failure boundaries, and simpler deployment patterns.

Unloading in .NET Core

A common reason developers miss AppDomains is unloading plugins. In .NET Core and later .NET, unloading is possible through collectible AssemblyLoadContext, but only when nothing still references the loaded types or assemblies.

That means plugin unloading is more explicit and often more constrained than the old AppDomain mental model. The runtime did not remove the need entirely. It replaced it with a more focused mechanism.

Security and Isolation Changed Too

AppDomains were once discussed alongside in-process security boundaries, but modern .NET does not treat them as the right answer for untrusted code isolation. If code is truly untrusted, separate processes, containers, permissions, and OS-level controls are the safer direction.

That shift is part of the reason AppDomains feel more historical than foundational in current .NET.

Common Pitfalls

A common mistake is assuming AssemblyLoadContext does everything AppDomains did. It does not. It mainly addresses assembly loading behavior.

Another issue is trying to port old AppDomain-based plugin systems line by line. Usually the better move is to rethink the boundary as either AssemblyLoadContext or an out-of-process worker.

Developers also sometimes expect collectible contexts to unload immediately. They only unload when the runtime can prove nothing still references the loaded code.

Finally, if the real need is fault isolation or trust isolation, in-process mechanisms are usually the wrong target. Use process boundaries instead.

Summary

  • .NET Core dropped AppDomains to simplify the runtime and support modern cross-platform design.
  • Many AppDomain scenarios depended on older runtime features that were intentionally removed.
  • 'AssemblyLoadContext replaces assembly-loading use cases, not the full AppDomain model.'
  • Separate processes are the preferred answer for strong isolation.
  • Porting old AppDomain code usually requires redesign, not a one-to-one API substitution.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.