BLAZMVVM0002: ViewModelDefinition Attribute
Summary
ViewModels must be decorated with the [ViewModelDefinition] attribute to ensure proper dependency injection registration.
Description
This analyzer ensures that ViewModel classes that inherit from ViewModelBase are decorated with the [ViewModelDefinition] attribute. This attribute is essential for automatic service registration in the dependency injection container.
Severity
Warning - This should be addressed so the ViewModel is properly registered and resolved.
Why This Rule Exists
- Ensures ViewModels are registered in the DI container
- Specifies the service lifetime (Transient, Scoped, Singleton)
- Enables automatic ViewModel resolution in Views
- Prevents runtime errors when navigating to Views
✅ DO: Correct Usage
using Blazing.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
// ✅ Correct: Transient lifetime (default, recommended for most ViewModels)
[ViewModelDefinition(Lifetime = ServiceLifetime.Transient)]
public class ProductViewModel : ViewModelBase
{
public string Name { get; set; }
}
// ✅ Correct: Singleton lifetime (for app-wide state)
[ViewModelDefinition(Lifetime = ServiceLifetime.Singleton)]
public class AppStateViewModel : ViewModelBase
{
public bool IsAuthenticated { get; set; }
}
// ✅ Correct: Scoped lifetime (for request-scoped data)
[ViewModelDefinition(Lifetime = ServiceLifetime.Scoped)]
public class ShoppingCartViewModel : ViewModelBase
{
private readonly List<Item> _items = new();
}
// ✅ Correct: Abstract base ViewModels don't need the attribute
public abstract class BaseViewModel : ViewModelBase
{
// Shared logic
}
[ViewModelDefinition(Lifetime = ServiceLifetime.Transient)]
public class ConcreteViewModel : BaseViewModel
{
// Specific implementation
}
❌ DON'T: Incorrect Usage
using Blazing.Mvvm.ComponentModel;
// ❌ Wrong: Missing ViewModelDefinition attribute
public class ProductViewModel : ViewModelBase
{
public string Name { get; set; }
}
// ❌ Wrong: Wrong attribute used
[Serializable]
public class UserViewModel : ViewModelBase
{
public string Username { get; set; }
}
// ❌ Wrong: Manual service registration conflicts
// In Program.cs
services.AddTransient<OrderViewModel>(); // Don't do this!
// In ViewModel file
[ViewModelDefinition(Lifetime = ServiceLifetime.Singleton)]
public class OrderViewModel : ViewModelBase
{
// This creates a conflict!
}
How to Fix
Add the ViewModelDefinition Attribute
// Before
using Blazing.Mvvm.ComponentModel;
public class ProductViewModel : ViewModelBase
{
public string Name { get; set; }
}
// After
using Blazing.Mvvm.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
[ViewModelDefinition(Lifetime = ServiceLifetime.Transient)]
public class ProductViewModel : ViewModelBase
{
public string Name { get; set; }
}
Choosing the Right Lifetime
Transient (Recommended Default)
- New instance created each time the ViewModel is resolved
- Use for most ViewModels
- Prevents state leakage between navigations
[ViewModelDefinition(Lifetime = ServiceLifetime.Transient)]
public class ProductDetailsViewModel : ViewModelBase { }
Scoped
- Single instance per scope (request/circuit in Blazor)
- Use for shopping carts, user sessions
- Shared within the same user session
[ViewModelDefinition(Lifetime = ServiceLifetime.Scoped)]
public class ShoppingCartViewModel : ViewModelBase { }
Singleton
- Single instance for the entire application lifetime
- Use for app-wide state, settings
- Be careful with thread safety
[ViewModelDefinition(Lifetime = ServiceLifetime.Singleton)]
public class AppSettingsViewModel : ViewModelBase { }
Code Fix
This analyzer provides an automatic code fix that:
- Adds
[ViewModelDefinition(Lifetime = ServiceLifetime.Transient)]attribute - Adds the necessary using directives
Related Analyzers
- BLAZMVVM0001: ViewModelBase Inheritance - Ensures proper base class
- BLAZMVVM0013: MvvmOwningComponentBase Usage - For ViewModels with scoped dependencies