FractalDataWorks.Services.Connections 0.4.0-preview.6

This is a prerelease version of FractalDataWorks.Services.Connections.
The owner has unlisted this package. This could mean that the package is deprecated, has security vulnerabilities or shouldn't be used anymore.
dotnet add package FractalDataWorks.Services.Connections --version 0.4.0-preview.6
                    
NuGet\Install-Package FractalDataWorks.Services.Connections -Version 0.4.0-preview.6
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FractalDataWorks.Services.Connections" Version="0.4.0-preview.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FractalDataWorks.Services.Connections" Version="0.4.0-preview.6" />
                    
Directory.Packages.props
<PackageReference Include="FractalDataWorks.Services.Connections" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FractalDataWorks.Services.Connections --version 0.4.0-preview.6
                    
#r "nuget: FractalDataWorks.Services.Connections, 0.4.0-preview.6"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FractalDataWorks.Services.Connections@0.4.0-preview.6
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FractalDataWorks.Services.Connections&version=0.4.0-preview.6&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=FractalDataWorks.Services.Connections&version=0.4.0-preview.6&prerelease
                    
Install as a Cake Tool

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 IConfiguration binding
  • 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

  1. Compile-Time Discovery - Source generator scans for ConnectionTypeBase derivatives
  2. Cross-Assembly Support - Connection types in referenced NuGet packages are discovered via module initializers
  3. Deferred Freeze - Collection stays mutable until first access (ConfigureAll, RegisterAll, or All())
  4. 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

  1. Creation - Provider creates connection via factory
  2. Configuration - Configuration validated and applied
  3. Opening - Connection opened for use
  4. Usage - Commands executed through connection
  5. Closing - Connection closed gracefully
  6. 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 state
  • Created (1) - Initial state after construction
  • Opening (2) - Transitioning to open
  • Open (3) - Ready for operations
  • Executing (4) - Command in progress
  • Closing (5) - Transitioning to closed
  • Closed (6) - Successfully closed
  • Broken (7) - Error state
  • Disposed (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 interfaces
  • FractalDataWorks.Services.Abstractions - Service type base classes
  • FractalDataWorks.Collections.SourceGenerators - Source generation (analyzer)
  • FractalDataWorks.MessageLogging.SourceGenerators - Logging source generation (analyzer)
  • FractalDataWorks.Results - Railway-oriented result types
  • FractalDataWorks.Configuration - Configuration base classes
  • Microsoft.Extensions.Configuration.Binder - Configuration binding
  • Microsoft.Extensions.DependencyInjection.Abstractions - DI abstractions
  • Microsoft.Extensions.Logging.Abstractions - Logging abstractions

Best Practices

  1. Follow three-phase registration - Register services, build, initialize factories
  2. Never store IServiceProvider - Use it once during initialization, then discard
  3. Inject factory dependencies via constructor - Factories receive loggers, providers directly
  4. Check result.IsSuccess - All operations return IGenericResult
  5. Use configuration names - Resolve connections by name from configuration
  6. Implement proper disposal - Connections implement IDisposable

License

Copyright FractalDataWorks. All rights reserved.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
Loading failed