Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should Class Implement Both IAsyncDisposable and IDisposable?

For example I have this class:

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;

    public IProductRepository Products { get; private set; }
    public ICategoryRepository Categories { get; private set; }
    public IPhotoRepository Photos { get; private set; }

    public UnitOfWork(ApplicationDbContext context, IProductRepository productRepository,
        ICategoryRepository categoryRepository, IPhotoRepository photoRepository)
    {
        _context = context;
        Products = productRepository;
        Categories = categoryRepository;
        Photos = photoRepository;
    }

    public int Complete()
    {
        return _context.SaveChanges();
    }

    public Task<int> CompleteAsync()
    {
        return _context.SaveChangesAsync();
    }

    public void Dispose()
    {
        _context.Dispose();
    }

    public async ValueTask DisposeAsync()
    {
        await _context.DisposeAsync();
    }
}

With a such Interface:

public interface IUnitOfWork : IDisposable, IAsyncDisposable
{
    IProductRepository Products { get; }
    ICategoryRepository Categories { get; }
    IPhotoRepository Photos { get; }
    int Complete();
    Task<int> CompleteAsync();
}

Is it right to have both Async and Sync methods? Also what method will be called during disposal if I'm using DI in asp net core for example.

like image 512
Anon Anon Avatar asked May 28 '20 21:05

Anon Anon


People also ask

What is DisposeAsync?

The DisposeAsyncCore() method is intended to perform the asynchronous cleanup of managed resources or for cascading calls to DisposeAsync() . It encapsulates the common asynchronous cleanup operations when a subclass inherits a base class that is an implementation of IAsyncDisposable.


2 Answers

When should you implement only IDisposable?

IDispoable is the more standard and default interface, and supported by nearly all frameworks since the very beginning or soon thereafter. If your class or a derived class of your class doesn't need to release resources asynchronously then consider only implementing IDisposable. For example, SemaphoreSlim doesn't implement IAsyncDisposable.

When can you get away with only implementing IAsyncDisposable?

Note: This applies more to situations where you are in control or aware of how your objects will be instantiated and destroyed. For example, if you are writing an executable (as opposed to a library) wherein you are more in control of your overall environment and code's destiny.

You should implement IAsyncDisposable if you foresee that your class or a derived class of your class will need or may need to release resources asynchronously.

When should you implement both IAsyncDisposable and IDisposable?

Note: Below applies more as best practice and recommended approach for libraries as they are typically not in control of the code that creates instances of the types defined in the library.

Ideally, you should implement both IAsyncDisposable and IDisposable. In other words, IAsyncDisposable shouldn't be considered as a replacement for IDisposable.

Why?

  1. The dependency injection framework may or may not support IAsyncDisposable. Most, of course, do but not all dependency injection systems are created equal. If you are writing a library, you don't know how your objects will get created.
  2. If your code runs inside a container and the container is disposed synchronously then exceptions might be thrown (true for .NET Core dependency injection system).
  3. It's not as clean to create an object and then block on a DisposeAsync to make it sync. The caller's dispose call will be more like yourDisposableObject.DisposeAsync().GetAwaiter().GetResult().

Here's a quote from the official Microsoft MSDN.

It is typical when implementing the IAsyncDisposable interface that classes will also implement the IDisposable interface. A good implementation pattern of the IAsyncDisposable interface is to be prepared for either synchronous or asynchronous dispose. All of the guidance for implementing the dispose pattern also applies to the asynchronous implementation.

Other minor recommendations: Consider implementing your Dispose() and DisposeAsync() as virtual if you foresee your class's derived classes will have resources that may need disposing. Otherwise, consider making your class sealed.

Good examples

  1. Stephen Toub's Binary Writer class

References

  1. Alistair Evans blog
  2. MSDN
  3. Code Therapist
like image 150
user3613932 Avatar answered Sep 20 '22 14:09

user3613932


Regardless of your AspNetCore concern; yes, it is perfectly ok to have both Sync and Async methods implemented in one class.

But, having interface chains (one interface mandates to implement another one) is considered as not clean-code.

see this

like image 23
Rookie2.0 Avatar answered Sep 17 '22 14:09

Rookie2.0