What is a Service?
Overview
In Asv.Avalonia, a Service is a specialized, usually singleton component that provides shared logic and infrastructure across the entire application. While ViewModels handle the logic for specific UI pieces, Services handle "global" concerns that don't belong to any single view.
Core Concept
Services are the backbone of your application's infrastructure. They are designed to be:
Singletons: Most services exist as a single instance throughout the application's lifecycle.
Decoupled: They don't know about the UI (Views). They provide data or functionality that ViewModels can consume.
Injected: Services are managed by the MEF (Managed Extensibility Framework) and are injected into other components via constructors.
Key Characteristics
1. Lifecycle Management
Services can inherit from AsyncDisposableOnce. This allows them to perform asynchronous cleanup when the application shuts down, such as closing database connections or saving state.
2. Global State
Services are the perfect place to store state that needs to persist as the user navigates between pages. For example, the NavigationService keeps track of the navigation history, and the ThemeService remembers whether the user prefers Dark or Light mode.
3. Configuration
Some services need settings. By inheriting from ServiceWithConfigBase<TConfig>, a service automatically gains the ability to load and save its state using the framework's configuration system. See our LocalizationService for the example.
Common Built-in Services
The framework provides several essential services out of the box. For example:
Navigation Service: Manages page switching, back/forward history, and focus tracking.
Command Service: Centralizes command execution, global hotkeys, and Undo/Redo history.
Localization Service: Manages language switching and string translations.
Theme Service: Controls the visual appearance (Dark/Light themes, accent colors).
When to Create a Service?
You should consider creating a service when you have logic that:
Needs to be accessed from multiple ViewModels.
Manages a global resource (e.g., a hardware connection, a database, or a web API).
Needs to stay alive even when the current page is closed.
Handles cross-cutting concerns like logging, caching, or user preferences.
How to Define a Service
A typical service consists of an interface and an implementation marked with MEF attributes:
To use it, simply request it in a constructor:
Service Registration
Standard Registration (Automatic)
By default, Asv.Avalonia relies on MEF's convention-based registration. This is the method for most services.
Mark the Class: Add
[Export]and[Shared]attributes to your service implementation.Scan the Assembly: In
App.axaml.cs, the framework scans your assembly for these attributes.
If you create a service class, use MEF attributes on it:
Conditional Registration (Optional Services)
Sometimes a service should only be registered under specific conditions (e.g., specific configuration settings or OS environment). There are two ways to achieve this.
Method 1: The Exclusion Strategy (Recommended)
This is the preferred approach. You keep the MEF attributes ([Export], [Shared]) on your class, treating it as a standard service. However, during the container setup, you explicitly exclude it from registration based on your condition.
Method 2: Manual Registration
Alternatively, you can remove the MEF attributes ([Export], [Shared]) from the service class entirely. Since the scanner won't pick it up automatically, you must manually register the instance in the container.
Use this method when you need to register an instance that was created outside the container (e.g., passed from Program.cs or a legacy system).