Mastering the Disposable Pattern in .NET Core

Clean up resources with precision using the Disposable Pattern

Posted by Hüseyin Sekmenoğlu on March 09, 2021 Backend Development Programming Fundamentals

♻️ What Is the Disposable Pattern?

The Disposable Pattern in .NET Core is a design pattern used to release unmanaged resources like file handles, database connections or memory buffers. It relies on the IDisposable interface to define a standard way to free resources deterministically.

This pattern is essential when your object holds non-memory resources or references to classes that implement IDisposable.


🔧 When Should You Use It?

You should implement the Disposable Pattern when your class:

  • Opens files or network connections

  • Uses database or service clients

  • Works with native memory or device handles

  • Manages other disposable objects

For example, Stream, HttpClient, DbConnection and FileStream all implement IDisposable and must be handled correctly.


🧩 Basic Implementation

public class FileLogger : IDisposable
{
    private StreamWriter _writer;
    private bool _disposed = false;

    public FileLogger(string path)
    {
        _writer = new StreamWriter(path);
    }

    public void Log(string message)
    {
        if (_disposed) throw new ObjectDisposedException(nameof(FileLogger));
        _writer.WriteLine(message);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            _writer?.Dispose();
        }

        _disposed = true;
    }

    ~FileLogger()
    {
        Dispose(false);
    }
}

This structure ensures both managed and unmanaged resources are cleaned up properly.


🧠 Why Use Dispose(bool disposing)?

The overloaded Dispose(bool disposing) lets you distinguish between:

  • true: Called by user code via Dispose() or using

  • false: Called by the garbage collector from the finalizer

This ensures you do not try to access managed objects during finalization.


🚀 Using using for Automatic Disposal

The using statement simplifies disposal:

using (var logger = new FileLogger("log.txt"))
{
    logger.Log("Application started");
}

In .NET 6 and later, you can also use the using declaration:

using var logger = new FileLogger("log.txt");
logger.Log("App running");

This ensures disposal at the end of the scope without indentation or nesting.


🏗️ Disposable in ASP.NET Core

In ASP.NET Core, disposable services like DbContext or HttpClient are often managed by Dependency Injection (DI). Scoped and transient services are automatically disposed when the scope ends.

To register a disposable service:

services.AddScoped<IMyService, MyDisposableService>();

There is no need to manually call Dispose when using DI.


🚨 Common Mistakes to Avoid

  • Forgetting to call Dispose on objects that hold resources

  • Not using GC.SuppressFinalize, which causes performance penalties

  • Manually disposing services managed by DI container

  • Using IDisposable incorrectly in value types


✅ Summary

The Disposable Pattern ensures efficient resource management in .NET Core. Always implement it when handling unmanaged resources or other disposable objects. Use using or rely on DI to simplify cleanup. When applied properly, this pattern improves performance, prevents memory leaks and makes your applications more stable.

Understanding and using this pattern correctly is a key skill for any .NET developer aiming for clean and reliable code.