Skip to content

Resource Cleanup

santiago edited this page Jan 29, 2026 · 1 revision

Resource Cleanup

The framework provides built-in support for resource cleanup through the IDisposable pattern.

IDisposable Interface

Implement IDisposable for services that need cleanup (database connections, file handles, network connections, etc.):

from azfunc_boot import IDisposable

class DatabaseConnection(IDisposable):
    def __init__(self, connection_string: str):
        self.connection = create_connection(connection_string)
    
    async def dispose(self):
        # Cleanup resources
        await self.connection.close()
        self.connection = None

Automatic Disposal

Scoped services implementing IDisposable are automatically disposed when the scope ends:

# Register as scoped service
@register_service
def register_services(self):
    self.container.add_scoped(
        IDatabaseConnection,
        lambda: DatabaseConnection(connection_string="...")
    )

# In your controller
async def process_request(self, req: func.HttpRequest):
    # Scope is created
    db: IDatabaseConnection = self.container.get_service(IDatabaseConnection)
    
    # Use the connection...
    await db.execute_query("SELECT * FROM users")
    
    # When method completes, scope ends
    # dispose() is automatically called on db

When to Use IDisposable

Implement IDisposable for services that:

  • Open database connections
  • Create file handles
  • Establish network connections
  • Allocate unmanaged resources
  • Need cleanup logic

Example: Database Context

from azfunc_boot import IDisposable

class DatabaseContext(IDisposable):
    def __init__(self, connection_string: str):
        self.connection = None
        self.connection_string = connection_string
    
    async def connect(self):
        self.connection = await create_async_connection(self.connection_string)
    
    async def execute_query(self, query: str):
        if not self.connection:
            await self.connect()
        return await self.connection.execute(query)
    
    async def dispose(self):
        if self.connection:
            await self.connection.close()
            self.connection = None
            self._logger.info("Database connection closed")

Example: File Handler

from azfunc_boot import IDisposable

class FileProcessor(IDisposable):
    def __init__(self, file_path: str):
        self.file = open(file_path, 'r')
    
    def read(self):
        return self.file.read()
    
    async def dispose(self):
        if self.file:
            self.file.close()
            self.file = None

Disposal Order

When a scope ends, services are disposed in reverse order of creation:

  1. Last created service is disposed first
  2. First created service is disposed last

This ensures dependencies are disposed before the services that depend on them.

Manual Disposal

While automatic disposal is recommended, you can also manually dispose services:

# Get a scoped service
service = self.container.get_service(IService)

# Use it...

# Manually dispose (if needed)
if isinstance(service, IDisposable):
    await service.dispose()

However, this is usually not necessary as the framework handles disposal automatically.

Best Practices

  1. Always implement IDisposable for resources: If your service uses resources that need cleanup, implement IDisposable
  2. Use scoped lifetime: Services with resources should typically be registered as scoped
  3. Idempotent disposal: Make dispose() safe to call multiple times
  4. Async cleanup: Use async def dispose() for async cleanup operations

Related Topics

Clone this wiki locally