C
C#10mo ago
Unmei

How to effectively dispose unmanaged resources?

i have some questions about disposing objects in .NET. I work with a .NET API that connects to a SQL Server database via Entity Framework. Basically, the hierarchy of calls are: Controller -> Scoped Service -> Scoped Repository -> DbContext. As we are increasing number of users of the API and our application isn't very optmized, i went into studying about disposing objects, specially unmanaged resources (those not "deletable" by GC right? How to know if a object is unmanaged btw?). I wrote some questions in the code snippet bellow. As I'm still new to this (never needed to write explicitly dispose methods before), I'm open to opinions and experiences you all had in your projects and investigations!
// UserService and BookService are scoped services. Are these unmanaged resources?
public class UserService : IDisposable
{
private IBookService _bookService;
private IUserRepository _userRepository;

public UserService(
IBookService bookService,
IUserRepository userRepository
) {
_bookService = bookService;
_userRepository = userRepository;
}

// ... methods

public void Dispose()
{
// is it effective to use Dispose like that?
// are _bookService and _userRepository unmanageable resources?
_bookService?.Dispose();
_userRepository?.Dispose();

// is it needed/effective to set those to null?
_bookService = null;
_userRepository = null;
}
}

// UserRepository is scoped
public class UserRepository : IUserRepository, IDisposable
{
private readonly DbContext _context;

public UserRepository(DbContext context)
{
_context = context;
}

// ... methods

public void Dispose()
{
// is it effective to use Dispose like that?
_context?.Dispose();

// is it needed/effective to set it to null?
_context = null;
}
}
// UserService and BookService are scoped services. Are these unmanaged resources?
public class UserService : IDisposable
{
private IBookService _bookService;
private IUserRepository _userRepository;

public UserService(
IBookService bookService,
IUserRepository userRepository
) {
_bookService = bookService;
_userRepository = userRepository;
}

// ... methods

public void Dispose()
{
// is it effective to use Dispose like that?
// are _bookService and _userRepository unmanageable resources?
_bookService?.Dispose();
_userRepository?.Dispose();

// is it needed/effective to set those to null?
_bookService = null;
_userRepository = null;
}
}

// UserRepository is scoped
public class UserRepository : IUserRepository, IDisposable
{
private readonly DbContext _context;

public UserRepository(DbContext context)
{
_context = context;
}

// ... methods

public void Dispose()
{
// is it effective to use Dispose like that?
_context?.Dispose();

// is it needed/effective to set it to null?
_context = null;
}
}
4 Replies
jcotton42
jcotton4210mo ago
The container will handle disposing of what it injected In general you should only dispose something if you created it, or were explicitly given "ownership" of it Neither applies here @Unmei
Unmei
UnmeiOP10mo ago
Got it! I guess it's one less place to look for performance improvements When you "something you created", would it include objects created with using statements? Like using scopedService = serviceProvider.GetRequiredService<IUserService>() or even those are handled "automagically"?
jcotton42
jcotton4210mo ago
Yes And in that case the using will dispose for you
Cracker
Cracker10mo ago
UserRepository as scoped service, Dispose will be called end of the scope, therefore if UserRepository is injected and used from different services within scope, if its used with using block, second call might have exception because trying to access disposed object and beaware when using scoped service in singleton services as it will be constructed once in singleton service its generally good to create and dispose right before and after db queries, less overhead to manage connection/transaction lifetime like this as scoped service In current case, it might be good to check if connection is already closed/disposed, if so dont try to dispose again to prevent exception. And same for querying, if connection is null or closed, create new connection By doing this controls, also should consider open/close transactions
Want results from more Discord servers?
Add your server