Diagnostic analyzers and code fix providers for Splat dependency injection.
$ dotnet add package Splat.DependencyInjection.AnalyzerA high-performance C# source generator that produces compile-time dependency injection registrations for Splat. Eliminates runtime reflection, provides full native AOT support, and includes intelligent analyzers with automatic code fixes.
This source generator produces dependency injection registrations for Splat at compile-time based on your constructor and property injection requirements. It uses an incremental source generator to provide fast builds with zero runtime reflection overhead.
Key features:
Always Be NuGetting. Package contains:
| Package | NuGet |
|---|---|
| Splat.DependencyInjection.SourceGenerator |
Add the package to your project:
<PackageReference Include="Splat.DependencyInjection.SourceGenerator" Version="{latest version}" PrivateAssets="all" />
Note: The PrivateAssets="all" attribute prevents the source generator from being transitively referenced by projects that depend on yours. This is the recommended configuration for source generators.
Use the SplatRegistrations static class to register services. The source generator will detect these calls and generate the implementation at compile-time.
Transient Registration (New Instance Each Time)
using static Splat.SplatRegistrations;
// Register with interface and implementation
Register<IToaster, Toaster>();
Register<IMessageService, MessageService>();
// Register concrete type only (when no interface)
Register<DatabaseContext>();
Lazy Singleton Registration (Single Lazy Instance)
// Basic lazy singleton
RegisterLazySingleton<IDatabase, SqliteDatabase>();
// With thread safety mode
RegisterLazySingleton<ICache, MemoryCache>(LazyThreadSafetyMode.PublicationOnly);
Thread safety modes:
LazyThreadSafetyMode.ExecutionAndPublication (default) - Full thread safety with locksLazyThreadSafetyMode.PublicationOnly - Multiple threads may initialize, first winsLazyThreadSafetyMode.None - No thread safety (single-threaded scenarios only)Constant Registration (Pre-Created Instance)
// Register an existing instance
var config = new Configuration { ApiUrl = "https://api.example.com" };
RegisterConstant<IConfiguration>(config);
Named Contracts (Multiple Implementations)
// Register multiple implementations with different contracts
Register<ILogger, FileLogger>("file");
Register<ILogger, ConsoleLogger>("console");
Register<ILogger, CloudLogger>("cloud");
// Retrieve by contract
var fileLogger = resolver.GetService<ILogger>("file");
Call SetupIOC() once during application startup in each assembly that uses SplatRegistrations:
using Splat;
using static Splat.SplatRegistrations;
// In your application entry point
public class App
{
public void ConfigureServices()
{
// Register all dependencies
Register<IUserService, UserService>();
Register<IAuthService, AuthService>();
RegisterLazySingleton<IDatabase, AppDatabase>();
// Initialize the container (generates and executes registrations)
SetupIOC();
}
}
For unit tests, pass a custom resolver:
[Test]
public void TestDependencies()
{
var resolver = new ModernDependencyResolver();
SetupIOC(resolver); // Use test-specific resolver
var service = resolver.GetService<IUserService>();
Assert.NotNull(service);
}
The source generator automatically resolves constructor parameters.
Single Constructor
public class UserService : IUserService
{
private readonly IDatabase _database;
private readonly ILogger _logger;
// Automatically detected - no attribute needed
public UserService(IDatabase database, ILogger logger)
{
_database = database;
_logger = logger;
}
}
Multiple Constructors
Use [DependencyInjectionConstructor] to specify which constructor to use:
using static Splat.SplatRegistrations;
public class AuthService : IAuthService
{
private readonly IDatabase _database;
private readonly ILogger _logger;
// Empty constructor for testing
public AuthService()
{
_database = new InMemoryDatabase();
_logger = new NullLogger();
}
// Production constructor - marked for DI
[DependencyInjectionConstructor]
public AuthService(IDatabase database, ILogger logger)
{
_database = database;
_logger = logger;
}
}
If you forget the attribute with multiple constructors, the analyzer will warn you and offer a code fix to add it automatically.
Lazy Dependencies
Inject Lazy<T> for on-demand initialization:
public class ExpensiveService
{
private readonly Lazy<IDatabase> _database;
public ExpensiveService(Lazy<IDatabase> database)
{
_database = database; // Not initialized yet
}
public void DoWork()
{
// Database initialized only when first accessed
_database.Value.ExecuteQuery("...");
}
}
// Register the dependency as a lazy singleton
RegisterLazySingleton<IDatabase, AppDatabase>();
Register<IExpensiveService, ExpensiveService>();
Mark properties with [DependencyInjectionProperty] for initialization after construction.
using static Splat.SplatRegistrations;
public class ViewModelBase
{
// Property injection - must have public or internal setter
[DependencyInjectionProperty]
public INavigationService Navigation { get; set; }
[DependencyInjectionProperty]
public ILogger Logger { get; internal set; } // Internal setters supported
}
The analyzer will:
private set to public set or internal setusing Splat;
using static Splat.SplatRegistrations;
// Models
public interface IDatabase { }
public interface ILogger { }
public interface IUserService { }
public class SqliteDatabase : IDatabase { }
public class FileLogger : ILogger { }
public class UserService : IUserService
{
private readonly IDatabase _database;
// Constructor injection
public UserService(IDatabase database)
{
_database = database;
}
// Property injection
[DependencyInjectionProperty]
public ILogger Logger { get; set; }
}
// Application startup
public class Program
{
public static void Main()
{
// Register dependencies
RegisterLazySingleton<IDatabase, SqliteDatabase>();
Register<ILogger, FileLogger>();
Register<IUserService, UserService>();
// Initialize container
SetupIOC();
// Resolve services
var userService = Locator.Current.GetService<IUserService>();
}
}
The package includes intelligent analyzers that provide real-time feedback:
| Diagnostic ID | Severity | Description | Code Fix |
|---|---|---|---|
| SPLATDI001 | Warning | Multiple constructors without [DependencyInjectionConstructor] attribute | Adds attribute to selected constructor |
| SPLATDI002 | Error | Property with [DependencyInjectionProperty] lacks accessible setter | Changes setter to public or internal |
| SPLATDI003 | Error | Multiple constructors marked with [DependencyInjectionConstructor] | Manual fix required |
| SPLATDI004 | Error | Constructor marked with [DependencyInjectionConstructor] is not accessible | Changes to public or internal |
The analyzer detects issues in real-time and offers automatic fixes via Quick Actions (Ctrl+. or Cmd+.).
The source generator follows a four-step process:
SplatRegistrations.Register() calls during compilationGenerated code example:
// Generated by Splat.DependencyInjection.SourceGenerator
static partial void SetupIOCInternal(IDependencyResolver resolver)
{
// Transient registration
resolver.Register<IUserService>(() => new UserService(
(IDatabase)resolver.GetService(typeof(IDatabase)),
(ILogger)resolver.GetService(typeof(ILogger))
) {
Navigation = (INavigationService)resolver.GetService(typeof(INavigationService))
});
// Lazy singleton registration
{
var lazy = new Lazy<IDatabase>(() => new SqliteDatabase(),
LazyThreadSafetyMode.ExecutionAndPublication);
resolver.Register<Lazy<IDatabase>>(() => lazy);
resolver.Register<IDatabase>(() => lazy.Value);
}
}
Compared to reflection-based DI:
Generator doesn't seem to run?
Ensure you called SetupIOC() in your startup code. The generator only produces code for assemblies that use SplatRegistrations.
"Multiple constructors" warning?
Add [DependencyInjectionConstructor] to the constructor you want used. Use the Quick Fix to add automatically.
Property injection not working?
Ensure the property has [DependencyInjectionProperty] and a public or internal setter. The analyzer will warn if the setter is missing or inaccessible.
Lazy dependencies not resolving?
Make sure you registered the dependency with RegisterLazySingleton, not Register. Only lazy singletons can be injected as Lazy<T>.
Version 2.1.1 includes breaking changes:
ISourceGenerator to modern IIncrementalGeneratorNew features in 2.1.1:
If you have questions or need help:
splat tagPlease do not open GitHub issues for general support questions.
We welcome contributions! Here's how you can help:
See our contribution guidelines for details.
The core team members and contributors work on this project in their free time. If Splat.DI.SourceGenerator increases your productivity, please consider supporting the project:
This project is licensed under the MIT License - see the LICENSE file for details.