Overview
Savvyio.Extensions.DependencyInjection adds Microsoft Dependency Injection support on top of Savvyio.Core. Its public surface is centered on marker-aware service contracts and IServiceCollection extensions for data sources, persistent stores, repositories, units of work, marshallers, and messaging channels, so multiple implementations can coexist in one container without losing type-safe resolution.
The package also includes the higher-level AddSavvyIO composition entry point for dispatcher and handler registration. Source and tests show it scanning assemblies for handler and dispatcher types, optionally capturing a handler descriptor, and always adding an IServiceLocator, while leaving actual persistence and transport implementations to sibling packages such as the EF Core, Dapper, NATS, Queue Storage, RabbitMQ, and Simple Queue Service integrations.
Key APIs
IDataSource<TMarker> adds a marker-qualified version of IDataSource so one application can register more than one data source and resolve the intended implementation through Microsoft Dependency Injection. It extends the non-generic IDataSource contract together with IDependencyInjectionMarker<TMarker>.
IPersistentDataStore<T, TOptions, TMarker> composes the readable, writable, searchable, and deletable Savvy I/O data-store contracts into a marker-aware abstraction. T must be a reference type, and TOptions must derive from AsyncOptions with a public parameterless constructor.
IPersistentRepository<TEntity, TKey, TMarker> does the same for repository implementations. It layers marker-based DI resolution on top of the core persistent repository abstractions, and TEntity must implement IIdentity<TKey>.
AddDataSource<TService> registers an IDataSource implementation in IServiceCollection and forwards marker-specific interfaces when the service type implements IDependencyInjectionMarker<TMarker>. The method guards against a null service collection and defaults the registration lifetime to Singleton unless you override ServiceOptions.Lifetime.
AddDataStore<TService, T> and AddDataStore<TService, T, TOptions> register implementations of IPersistentDataStore<T, TOptions> and use a nested-type predicate that targets IDataStore<> interfaces. The tests show these registrations resolving both unmarked and marker-qualified persistent data-store contracts for the same DTO type.
AddRepository<TService, TEntity, TKey> and AddUnitOfWork<TService> register persistent repositories and units of work for domain entities. Repositories default to Scoped lifetime, and AddUnitOfWork promotes a marker-specific IUnitOfWork<TMarker> service type automatically when the implementation exposes a dependency-injection marker.
AddMessageQueue<TService, TRequest> and AddMessageBus<TService, TRequest> register point-to-point and publish-subscribe channels for IRequest message types. Both default to Singleton lifetime and expose the matching sender, receiver, publisher, or subscriber interfaces through their nested-type predicates.
AddSavvyIO is the composition entry point for dispatcher and handler registration. It applies SavvyioDependencyInjectionOptions, optionally scans assemblies for dispatcher and handler implementations, adds handler descriptors when enabled, registers discovered dispatcher services, and then adds the configured IServiceLocator.
SavvyioDependencyInjectionOptions controls how AddSavvyIO behaves. Source and tests show default values of false for dispatcher discovery, handler discovery, and handler service descriptors, Transient lifetimes for the service locator, handlers, and dispatchers, and an initially empty assembly scan list that you populate with AddAssemblyToScan or AddAssemblyRangeToScan.
Basic usage
using Codebelt.Extensions.Xunit;
using Microsoft.Extensions.DependencyInjection;
using Savvyio.Extensions.DependencyInjection;
using Xunit;
namespace MyProject.Tests;
public class DataSourceRegistrationTest : Test
{
public DataSourceRegistrationTest(ITestOutputHelper output) : base(output) { }
[Fact]
public void AddDataSource_ConfiguredMarker_ResolvesServices()
{
var services = new ServiceCollection();
services.AddDataSource<OrdersDataSource>();
services.AddDataSource<BillingDataSource>();
using var provider = services.BuildServiceProvider();
var orders = provider.GetRequiredService<IDataSource<OrdersMarker>>();
var billing = provider.GetRequiredService<IDataSource<BillingMarker>>();
TestOutput.WriteLine($"Orders source: {orders.GetType().Name}");
TestOutput.WriteLine($"Billing source: {billing.GetType().Name}");
Assert.IsType<OrdersDataSource>(orders);
Assert.IsType<BillingDataSource>(billing);
Assert.NotSame(orders, billing);
}
private sealed class OrdersMarker;
private sealed class BillingMarker;
private sealed class OrdersDataSource : IDataSource<OrdersMarker> { }
private sealed class BillingDataSource : IDataSource<BillingMarker> { }
}
Use this pattern when one application needs multiple Savvy I/O data sources and the consuming code must resolve the correct implementation explicitly instead of depending on registration order.
It matters because the marker-aware contracts and registration helpers are the package's main value over plain Microsoft Dependency Injection service registrations.
Installation
dotnet add package Savvyio.Extensions.DependencyInjection
Usage guidance
Adopt this package when your application already uses Savvy I/O abstractions and you want Microsoft Dependency Injection registrations that stay explicit across handlers, dispatchers, data access components, and message channels, especially when more than one implementation of the same abstraction must live in the same container. If you only need ordinary framework service registration, plain IServiceCollection extensions may be enough, and if you need concrete storage or transport behavior, choose one of the sibling integration packages because this package only contributes the DI-facing contracts and registration layer.
Family packages
- 🏭Savvyio.App
- 📦Savvyio.Commands
- 📦Savvyio.Commands.Messaging
- 📦Savvyio.Core
- 📦Savvyio.Domain
- 📦Savvyio.Domain.EventSourcing
- 📦Savvyio.EventDriven
- 📦Savvyio.EventDriven.Messaging
- 📦Savvyio.Extensions.Dapper
- 📦Savvyio.Extensions.DapperExtensions
- 📦Savvyio.Extensions.DependencyInjection.Dapper
- 📦Savvyio.Extensions.DependencyInjection.DapperExtensions
- 📦Savvyio.Extensions.DependencyInjection.Domain
- 📦Savvyio.Extensions.DependencyInjection.EFCore
- 📦Savvyio.Extensions.DependencyInjection.EFCore.Domain
- 📦Savvyio.Extensions.DependencyInjection.EFCore.Domain.EventSourcing
- 📦Savvyio.Extensions.DependencyInjection.NATS
- 📝Savvyio.Extensions.DependencyInjection.Newtonsoft.Json
- 📦Savvyio.Extensions.DependencyInjection.QueueStorage
- 📦Savvyio.Extensions.DependencyInjection.RabbitMQ
- 📦Savvyio.Extensions.DependencyInjection.SimpleQueueService
- 📝Savvyio.Extensions.DependencyInjection.Text.Json
- 📦Savvyio.Extensions.Dispatchers
- 📦Savvyio.Extensions.EFCore
- 📦Savvyio.Extensions.EFCore.Domain
- 📦Savvyio.Extensions.EFCore.Domain.EventSourcing
- 📦Savvyio.Extensions.NATS
- 📝Savvyio.Extensions.Newtonsoft.Json
- 📦Savvyio.Extensions.QueueStorage
- 📦Savvyio.Extensions.RabbitMQ
- 📦Savvyio.Extensions.SimpleQueueService
- 📝Savvyio.Extensions.Text.Json
- 📦Savvyio.Messaging
- 📦Savvyio.Queries