FractalDataWorks.Services.Connections
0.4.0-preview.6
dotnet add package FractalDataWorks.Services.Connections --version 0.4.0-preview.6
NuGet\Install-Package FractalDataWorks.Services.Connections -Version 0.4.0-preview.6
<PackageReference Include="FractalDataWorks.Services.Connections" Version="0.4.0-preview.6" />
<PackageVersion Include="FractalDataWorks.Services.Connections" Version="0.4.0-preview.6" />
<PackageReference Include="FractalDataWorks.Services.Connections" />
paket add FractalDataWorks.Services.Connections --version 0.4.0-preview.6
#r "nuget: FractalDataWorks.Services.Connections, 0.4.0-preview.6"
#:package FractalDataWorks.Services.Connections@0.4.0-preview.6
#addin nuget:?package=FractalDataWorks.Services.Connections&version=0.4.0-preview.6&prerelease
#tool nuget:?package=FractalDataWorks.Services.Connections&version=0.4.0-preview.6&prerelease
FractalDataWorks.Services.Connections
Connection management framework providing provider-based abstractions for external connections with source-generated type collections.
Overview
This package implements a provider-based model for managing connections to external systems (databases, APIs, message queues, etc.). Target framework: net10.0.
Features
- DefaultConnectionProvider - Provider for creating and managing connections
- ConnectionTypes Collection - Source-generated ServiceTypeCollection of connection types
- Configuration Integration - Support for
IConfigurationbinding - State Management - Connection state tracking via TypeCollection
- Three-Phase Registration - Singleton factories with scoped dependency context
Installation
<PackageReference Include="FractalDataWorks.Services.Connections" Version="1.0.0" />
Quick Start
1. Register Services (Three-Phase Pattern)
From Reference Solution FileConnectionTypes.cs:17-28:
/// <summary>
/// Phase 1: Register all file connection type services with DI.
/// </summary>
public static void RegisterAll(IServiceCollection services)
{
// Register the provider as singleton
services.AddSingleton<FileConnectionProvider>();
// Register each connection type's services
foreach (var type in All())
{
type.RegisterServices(services);
}
}
From Reference Solution FileConnectionTypes.cs:33-43:
/// <summary>
/// Phase 2: Initialize factories by resolving from DI and registering with provider.
/// </summary>
public static void InitializeFactories(IServiceProvider serviceProvider)
{
var provider = serviceProvider.GetRequiredService<FileConnectionProvider>();
foreach (var type in All())
{
type.RegisterFactory(provider, serviceProvider);
}
// CRITICAL: serviceProvider is NOT stored!
}
2. Configure Connections
Configuration is loaded from IConfiguration sections. From DefaultConnectionProvider.cs:31-44:
/// <para>
/// Example configuration (flat structure):
/// <code>
/// {
/// "Connections": {
/// "OrdersDb": {
/// "ConnectionType": "MsSql",
/// "SecretManagerName": "AzureKeyVault",
/// "Server": "localhost",
/// "Database": "Orders",
/// "Port": 1433,
/// "Authentication": { "Type": "SqlAuth", "Username": "sa" }
/// }
/// }
/// }
/// </code>
/// </para>
3. Use Connections
From Reference Solution Program.cs:103-121:
// --- CSV Connection ---
Console.WriteLine(" --- Creating CSV Connection ---");
var csvConfig = new CsvConnectionConfiguration
{
Name = "SalesData",
ConnectionType = "Csv",
FilePath = "/data/sales.csv",
HasHeader = true,
Delimiter = ","
};
var csvResult = provider.CreateConnection(csvConfig);
if (csvResult.IsSuccess)
{
var csvConnection = csvResult.Value!;
Console.WriteLine($" Created: {csvConnection.Name} ({csvConnection.ConnectionType})");
Console.WriteLine($" FilePath: {csvConfig.FilePath}");
Console.WriteLine($" HasHeader: {csvConfig.HasHeader}");
}
Key Components
IConnectionProvider
From IConnectionProvider.cs:12-60:
/// <summary>
/// Defines the contract for connection providers in the FractalDataWorks framework.
/// The provider uses ConnectionTypes to lookup configuration types and factories.
/// Follows Railway-Oriented Programming - all operations return Results.
/// Inherits from IFdwServiceProvider for standardized service creation.
/// </summary>
public interface IConnectionProvider : IFdwServiceProvider<IGenericConnection, IConnectionConfiguration>
{
// Inherits from IFdwServiceProvider:
// IGenericResult<IGenericConnection> Create(IConnectionConfiguration configuration);
// IGenericResult<IGenericConnection> Create(string name);
// IGenericResult<IGenericConnection> Create(Guid id);
// IGenericResult<T> Create<T>(IConnectionConfiguration configuration) where T : IGenericConnection;
// IGenericResult<T> Create<T>(string name) where T : IGenericConnection;
// IGenericResult<T> Create<T>(Guid id) where T : IGenericConnection;
}
DefaultConnectionProvider
From DefaultConnectionProvider.cs:48-67:
/// <summary>
/// Implementation of IConnectionProvider that acts as a mini-IoC container.
/// Inherits from ServiceProvider to provide service resolution without depending on the main IoC container at runtime.
/// </summary>
public sealed class DefaultConnectionProvider : ServiceProvider, IConnectionProvider, IDataConnectionProvider
{
private readonly Dictionary<string, IConnectionFactory> _factories = new(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Func<string, IConnectionConfiguration?>> _configLoaders = new(StringComparer.OrdinalIgnoreCase);
private readonly ConcurrentDictionary<string, IConnectionConfiguration> _configurationCache = new(StringComparer.OrdinalIgnoreCase);
private readonly ILogger<DefaultConnectionProvider> _logger;
private readonly IConfiguration _configuration;
public DefaultConnectionProvider(
ILogger<DefaultConnectionProvider> logger,
IConfiguration configuration)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
ConnectionTypes (Source-Generated)
From ConnectionTypes.cs:11-24:
/// <summary>
/// Collection of connection service types.
/// Generated by ServiceTypeCollectionGenerator with high-performance lookups.
/// </summary>
[ExcludeFromCodeCoverage]
[ServiceTypeCollection(
typeof(ConnectionTypeBase<IGenericConnection, IConnectionFactory<IGenericConnection, IConnectionConfiguration>, IConnectionConfiguration>),
typeof(IConnectionType),
typeof(ConnectionTypes),
GenerateProvider = true,
ServiceInterface = typeof(IGenericConnection),
ConfigurationInterface = typeof(IConnectionConfiguration),
ProviderType = typeof(DefaultConnectionProvider),
ProviderInterface = typeof(IConnectionProvider))]
public partial class ConnectionTypes : ServiceTypeCollectionBase<...>
Connection Type Discovery and Registration
The framework uses source generation with a deferred freeze pattern for cross-assembly type discovery.
The Cross-Assembly Problem
Connection types like MsSqlConnectionType are in separate NuGet packages. Static constructors only run when that assembly is accessed. If you call ConnectionTypes.All() before loading MsSql, it won't be included.
The Solution: Module Initializers
The Registration source generator creates module initializers in consuming assemblies that register all connection types from referenced packages:
sequenceDiagram
participant CLR as .NET Runtime
participant Init as Module Initializer
participant Col as ConnectionTypes
participant App as Program.cs
Note over CLR: Assembly loads (before Main)
CLR->>Init: [ModuleInitializer] runs
Init->>Col: RegisterMember(new MsSqlConnectionType())
Note over Col: Added to pending list
Note over App: ConfigureServices
App->>Col: ConnectionTypes.Configure(services, config)
Col->>Col: EnsureFrozen()
Note over Col: Collection freezes with MsSql
App->>Col: ConnectionTypes.Register(services)
Note over App: All connection types registered
Complete Startup Flow
var builder = WebApplication.CreateBuilder(args);
// Optional: Create logger for startup diagnostics (before DI is built)
using var loggerFactory = LoggerFactory.Create(b => b.AddConsole());
// Phase 1: Configure (binds IOptions from configuration)
// Module initializers have already run, so MsSql is registered
ConnectionTypes.Configure(builder.Services, builder.Configuration, loggerFactory);
// Phase 1: Register (adds factories to DI)
ConnectionTypes.Register(builder.Services, loggerFactory);
var app = builder.Build();
// Phase 2: Initialize (optional, for eager loading)
ConnectionTypes.Initialize(app.Services, loggerFactory);
app.Run();
Note:
Initialize()is optional. When called, it eagerly resolves all factories at startup. When omitted, factories resolve lazily on first use.
How It Works
- Compile-Time Discovery - Source generator scans for
ConnectionTypeBasederivatives - Cross-Assembly Support - Connection types in referenced NuGet packages are discovered via module initializers
- Deferred Freeze - Collection stays mutable until first access (
ConfigureAll,RegisterAll, orAll()) - Three-Phase Registration - Factories registered as singletons, initialized after build
Transitive Generator Flow
The Registration generator is embedded in FractalDataWorks.Services.Connections and flows transitively to consumers:
flowchart LR
subgraph FDW["FractalDataWorks Packages"]
Conn[Services.Connections] -->|embeds| Reg[Registration Generator]
MsSql[Services.Connections.MsSql] -->|references| Conn
end
subgraph Consumer["Your Application"]
App[Your.Api] -->|PackageReference| MsSql
App -.->|transitively receives| Reg
Reg -->|generates| Init[Module Initializer]
end
Consumers don't need to reference any generator directly - it flows through the package dependency chain.
Creating Custom Connection Types
1. Create the Connection Type Base
From ConnectionTypeBase.cs:30-55:
/// <summary>
/// Base class for connection service type definitions that inherit from ServiceTypeBase.
/// Provides metadata and factory creation for connection services with typed provider support.
/// </summary>
public abstract class ConnectionTypeBase<TService, TFactory, TConfiguration> :
ServiceTypeBase<TService, TFactory, TConfiguration, DefaultConnectionProvider>,
IConnectionType<TService, TConfiguration, TFactory>
where TService : class, IGenericConnection
where TConfiguration : class, IConnectionConfiguration
where TFactory : class, IConnectionFactory<TService, TConfiguration>
{
protected ConnectionTypeBase(
string name,
string sectionName,
string displayName,
string description,
OptionsLoaderBase? optionsLoader = null,
string? category = null)
: base(name, sectionName, displayName, description, optionsLoader ?? OptionsLoaderTypes.Reloadable, category ?? "Connection")
{
}
2. Implement the Connection Base
From ConnectionBase.cs:36-58:
/// <summary>
/// Abstract base class for all data connection service implementations.
/// Handles IDataCommand -> TCommand translation and execution.
/// </summary>
public abstract class ConnectionBase<TCommand, TConfiguration, TService>
: ServiceBase<IDataCommand, TConfiguration, TService>, IDataConnection
where TConfiguration : class, IConnectionConfiguration
where TService : class
{
private IConnectionState _state;
private readonly IDataCommandTranslator<TCommand> _translator;
protected ConnectionBase(
ILogger logger,
TConfiguration configuration,
IDataCommandTranslator<TCommand> translator)
: base(logger, configuration)
{
_state = ConnectionStates.Created;
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
}
Connection Lifecycle
- Creation - Provider creates connection via factory
- Configuration - Configuration validated and applied
- Opening - Connection opened for use
- Usage - Commands executed through connection
- Closing - Connection closed gracefully
- Disposal - Resources cleaned up
State Management
Connections track their state through the ConnectionStates TypeCollection. States are defined in the Abstractions package.
Available states (from /States/*.cs in Abstractions):
Unknown(0) - Default/unknown stateCreated(1) - Initial state after constructionOpening(2) - Transitioning to openOpen(3) - Ready for operationsExecuting(4) - Command in progressClosing(5) - Transitioning to closedClosed(6) - Successfully closedBroken(7) - Error stateDisposed(8) - Final state after disposal
Logging
Logging uses the MessageLogging source generator pattern.
From ConnectionProviderLogger.cs:19-25:
[MessageLogging(
EventId = 7101,
Level = LogLevel.Debug,
Message = "Getting connection for type: {connectionType}")]
public static partial IGenericMessage GettingConnection(
ILogger logger,
string connectionType);
From ConnectionProviderLogger.cs:47-53:
[MessageLogging(
EventId = 7103,
Level = LogLevel.Error,
Message = "No factory registered for connection type: {connectionType}")]
public static partial IGenericMessage NoFactoryRegistered(
ILogger logger,
string connectionType);
Dependencies
From FractalDataWorks.Services.Connections.csproj:
FractalDataWorks.Services.Connections.Abstractions- Connection interfacesFractalDataWorks.Services.Abstractions- Service type base classesFractalDataWorks.Collections.SourceGenerators- Source generation (analyzer)FractalDataWorks.MessageLogging.SourceGenerators- Logging source generation (analyzer)FractalDataWorks.Results- Railway-oriented result typesFractalDataWorks.Configuration- Configuration base classesMicrosoft.Extensions.Configuration.Binder- Configuration bindingMicrosoft.Extensions.DependencyInjection.Abstractions- DI abstractionsMicrosoft.Extensions.Logging.Abstractions- Logging abstractions
Best Practices
- Follow three-phase registration - Register services, build, initialize factories
- Never store IServiceProvider - Use it once during initialization, then discard
- Inject factory dependencies via constructor - Factories receive loggers, providers directly
- Check result.IsSuccess - All operations return
IGenericResult - Use configuration names - Resolve connections by name from configuration
- Implement proper disposal - Connections implement
IDisposable
Related Packages
- FractalDataWorks.Services.Connections.Abstractions - Interfaces and base types
- FractalDataWorks.Services.Abstractions - ServiceType framework
- FractalDataWorks.Collections - TypeCollection framework
License
Copyright FractalDataWorks. All rights reserved.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FractalDataWorks.Commands.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Commands.Data.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Configuration (>= 0.4.0-preview.6)
- FractalDataWorks.Configuration.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Data.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.MessageLogging.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Results (>= 0.4.0-preview.6)
- FractalDataWorks.Schema.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Services (>= 0.4.0-preview.6)
- FractalDataWorks.Services.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Services.Authentication.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Services.Connections.Abstractions (>= 0.4.0-preview.6)
- FractalDataWorks.Services.SecretManagers.Abstractions (>= 0.4.0-preview.6)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|