Nabs.Launchpad.Core.Persistence 10.0.219

Prefix Reserved
dotnet add package Nabs.Launchpad.Core.Persistence --version 10.0.219
                    
NuGet\Install-Package Nabs.Launchpad.Core.Persistence -Version 10.0.219
                    
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="Nabs.Launchpad.Core.Persistence" Version="10.0.219" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nabs.Launchpad.Core.Persistence" Version="10.0.219" />
                    
Directory.Packages.props
<PackageReference Include="Nabs.Launchpad.Core.Persistence" />
                    
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 Nabs.Launchpad.Core.Persistence --version 10.0.219
                    
#r "nuget: Nabs.Launchpad.Core.Persistence, 10.0.219"
                    
#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 Nabs.Launchpad.Core.Persistence@10.0.219
                    
#: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=Nabs.Launchpad.Core.Persistence&version=10.0.219
                    
Install as a Cake Addin
#tool nuget:?package=Nabs.Launchpad.Core.Persistence&version=10.0.219
                    
Install as a Cake Tool

Nabs Launchpad Core Persistence Library

The Nabs Launchpad Core Persistence library provides flexible database configuration and repository patterns for Entity Framework Core applications. This library supports SQL Server (local containers and Azure), Cosmos DB, and in-memory storage with environment-aware connection string management.

Key Features

  • Multi-Database Support: SQL Server, Azure SQL, Cosmos DB, and In-Memory databases
  • Environment-Aware Configuration: Automatic detection of Azure vs local environments
  • Azure Identity Integration: Passwordless authentication for Azure SQL databases
  • Local Container Support: Easy configuration for local SQL Server containers
  • DbContext Factory Pattern: Proper lifetime management for EF Core contexts
  • In-Memory Repository: Simple concurrent dictionary-based repository for testing
  • Fluent Configuration API: Easy-to-use extension methods for database setup
  • Design-Time Support: Integration with EF Core migrations and tooling

Core Components

ProjectExtensions

Provides extension methods (AddSqlDb) for configuring SQL Server and Azure SQL databases with automatic environment detection.

ISqlConnectionStringProvider

Interface for providing connection strings with different implementations for various scenarios.

SqlConnectionStringProvider

Direct connection string provider for explicit connection string configuration.

SqlLocalContainerConnectionStringProvider

Provides connection strings for local SQL Server containers (e.g., Docker containers).

SqlAzureIdentityConnectionStringProvider

Provides connection strings for Azure SQL with Azure Identity authentication (passwordless).

SqlDbContextFactory

Factory for creating DbContext instances with proper connection string resolution.

InMemoryRepository<TId, TItem>

Abstract base class for in-memory repositories using concurrent dictionaries.

Usage Examples

Basic SQL Server Setup with Connection String

using Nabs.Launchpad.Core.Persistence;

var builder = WebApplication.CreateBuilder(args);

// Simple connection string configuration
builder.Services.AddSqlDb<MyDbContext>(
    builder.Configuration.GetConnectionString("DefaultConnection")!
);

var app = builder.Build();

Local Container Configuration

// For local development with Docker SQL Server
builder.Services.AddSqlDb<MyDbContext>(
    sqlContainerConfig: options =>
    {
        options.Server = "localhost,1433";
        options.Database = "MyDatabase";
        options.UserId = "sa";
        options.Password = "YourStrong@Password";
        options.TrustServerCertificate = true;
    },
    sqlAzureConfig: null  // Not used locally
);

Azure SQL with Managed Identity

// Automatically uses Azure Identity when deployed to Azure
builder.Services.AddSqlDb<MyDbContext>(
    sqlContainerConfig: options =>
    {
        // Local development configuration
        options.Server = "localhost,1433";
        options.Database = "MyDatabase";
        options.UserId = "sa";
        options.Password = "YourStrong@Password";
    },
    sqlAzureConfig: options =>
    {
        // Azure production configuration
        options.Server = "myserver.database.windows.net";
        options.Database = "MyDatabase";
        // No password needed - uses Azure Identity
    }
);

Environment-Aware Configuration

// The library automatically detects Azure environment
// Based on WEBSITE_SITE_NAME or IDENTITY_ENDPOINT environment variables

builder.Services.AddSqlDb<MyDbContext>(
    sqlContainerConfig: localOptions =>
    {
        // Used when NOT in Azure
        localOptions.Server = builder.Configuration["Sql:Server"]!;
        localOptions.Database = builder.Configuration["Sql:Database"]!;
        localOptions.UserId = builder.Configuration["Sql:UserId"]!;
        localOptions.Password = builder.Configuration["Sql:Password"]!;
    },
    sqlAzureConfig: azureOptions =>
    {
        // Used when IN Azure
        azureOptions.Server = builder.Configuration["Sql:AzureServer"]!;
        azureOptions.Database = builder.Configuration["Sql:Database"]!;
    }
);

Using DbContext Factory

public class MyService
{
    private readonly IDbContextFactory<MyDbContext> _contextFactory;
    
    public MyService(IDbContextFactory<MyDbContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }
    
    public async Task<List<User>> GetUsersAsync()
    {
        // Create a new context instance
        await using var context = await _contextFactory.CreateDbContextAsync();
        
        return await context.Users.ToListAsync();
    }
}

In-Memory Repository Pattern

// Define your repository
public class UserRepository : InMemoryRepository<string, User>
{
    public async Task<Result<User>> GetByEmailAsync(string email)
    {
        var users = await GetAll();
        if (!users.IsSuccess)
            return Result.NotFound();
        
        var user = users.Value.FirstOrDefault(u => u.Email == email);
        return user is not null 
            ? Result.Success(user) 
            : Result.NotFound();
    }
}

// Use the repository
var repository = new UserRepository();

// Add
var user = new User { Id = "1", Name = "John", Email = "john@example.com" };
await repository.Add(user.Id, user);

// Get
var result = await repository.GetById("1");
if (result.IsSuccess)
{
    Console.WriteLine($"Found: {result.Value.Name}");
}

// Update
user.Name = "John Updated";
await repository.Update(user.Id, user);

// Delete
await repository.Delete(user.Id);

// Get all
var allUsers = await repository.GetAll();

Configuration from appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyDb;User Id=sa;Password=Pass123;TrustServerCertificate=true"
  },
  "Sql": {
    "Server": "localhost,1433",
    "Database": "MyDatabase",
    "UserId": "sa",
    "Password": "YourStrong@Password",
    "AzureServer": "myserver.database.windows.net"
  }
}

Multiple DbContexts

// Register multiple database contexts
builder.Services.AddSqlDb<OrdersDbContext>(
    builder.Configuration.GetConnectionString("OrdersDb")!
);

builder.Services.AddSqlDb<CustomersDbContext>(
    builder.Configuration.GetConnectionString("CustomersDb")!
);

// Use in services
public class MyService
{
    private readonly IDbContextFactory<OrdersDbContext> _ordersFactory;
    private readonly IDbContextFactory<CustomersDbContext> _customersFactory;
    
    public MyService(
        IDbContextFactory<OrdersDbContext> ordersFactory,
        IDbContextFactory<CustomersDbContext> customersFactory)
    {
        _ordersFactory = ordersFactory;
        _customersFactory = customersFactory;
    }
}

API Reference

ProjectExtensions

AddSqlDb<TDbContext> (with configurations)
public static IServiceCollection AddSqlDb<TDbContext>(
    this IServiceCollection services,
    Action<SqlLocalContainerOptions>? sqlContainerConfig,
    Action<SqlAzureIdentityOptions>? sqlAzureConfig)
    where TDbContext : DbContext

Registers a SQL database context with environment-aware configuration.

Parameters:

  • sqlContainerConfig: Configuration for local SQL Server container
  • sqlAzureConfig: Configuration for Azure SQL with managed identity

Behavior:

  • Detects Azure environment automatically
  • Uses Azure config when in Azure, local config otherwise
  • Registers both IDbContextFactory<TDbContext> and IDesignTimeDbContextFactory<TDbContext>
AddSqlDb<TDbContext> (with connection string)
public static IServiceCollection AddSqlDb<TDbContext>(
    this IServiceCollection services,
    string connectionString)
    where TDbContext : DbContext

Registers a SQL database context with an explicit connection string.

Parameters:

  • connectionString: The connection string to use

ISqlConnectionStringProvider

public interface ISqlConnectionStringProvider
{
    string GetConnectionString();
}

Interface for providing database connection strings.

InMemoryRepository<TId, TItem>

Abstract base class for in-memory repositories using ConcurrentDictionary.

Add
public async Task<Result<TItem>> Add(TId id, TItem entity)

Adds a new entity to the repository.

GetById
public async Task<Result<TItem>> GetById(TId id)

Retrieves an entity by its ID. Returns Result.NotFound() if not found.

GetAll
public async Task<Result<IEnumerable<TItem>>> GetAll()

Retrieves all entities in the repository.

Update
public async Task<Result<TItem>> Update(TId id, TItem entity)

Updates an existing entity. Returns error if entity doesn't exist.

Delete
public async Task<Result> Delete(TId id)

Deletes an entity by ID. Returns Result.NotFound() if not found.

Store
public readonly ConcurrentDictionary<TId, TItem> Store

Direct access to the underlying concurrent dictionary for advanced scenarios.

SqlLocalContainerOptions

Configuration options for local SQL Server containers.

Properties:

  • Server: Server address (e.g., "localhost,1433")
  • Database: Database name
  • UserId: User ID for authentication
  • Password: Password for authentication
  • TrustServerCertificate: Whether to trust server certificate (typically true for local)

SqlAzureIdentityOptions

Configuration options for Azure SQL with managed identity.

Properties:

  • Server: Azure SQL server address (e.g., "myserver.database.windows.net")
  • Database: Database name

Note: No password required - uses Azure Identity authentication.

Environment Detection

The library automatically detects Azure environments by checking:

var isAzure = !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")) ||
              !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT"));

Azure Indicators:

  • WEBSITE_SITE_NAME: Set in Azure App Service
  • IDENTITY_ENDPOINT: Set when Azure Managed Identity is available

Azure Identity Authentication

When deployed to Azure, the library uses passwordless authentication:

  1. Managed Identity: Uses the app's managed identity
  2. Azure AD Authentication: No passwords stored in configuration
  3. Automatic Token Management: Tokens automatically refreshed

Azure SQL Configuration:

-- Grant permissions to managed identity
CREATE USER [your-app-name] FROM EXTERNAL PROVIDER;
ALTER ROLE db_datareader ADD MEMBER [your-app-name];
ALTER ROLE db_datawriter ADD MEMBER [your-app-name];

EF Core Migrations

The library supports EF Core design-time tools:

# Add migration
dotnet ef migrations add InitialCreate --project YourProject

# Update database
dotnet ef database update --project YourProject

The IDesignTimeDbContextFactory<TDbContext> registration enables migrations to work correctly.

Best Practices

Connection String Security

  1. Never commit passwords: Use user secrets or environment variables
  2. Use Azure Identity: Prefer managed identity over connection strings in Azure
  3. Rotate passwords: Regularly rotate local development passwords

DbContext Lifetime

  1. Use Factory Pattern: Always use IDbContextFactory<T> for long-lived services
  2. Dispose Contexts: Use await using to properly dispose contexts
  3. Avoid Sharing: Don't share DbContext instances across requests

In-Memory Repository

  1. Testing Only: Use for unit tests and prototyping
  2. Not Thread-Safe Operations: While dictionary is thread-safe, your operations might not be
  3. No Persistence: Data lost when application stops

Configuration

  1. Environment-Specific: Use different configurations for dev, staging, production
  2. Validate Early: Ensure connection strings are valid at startup
  3. Use Configuration: Prefer configuration over hardcoded values

Testing

Using In-Memory Repository in Tests

[Fact]
public async Task Repository_AddAndRetrieve_Success()
{
    // Arrange
    var repository = new UserRepository();
    var user = new User { Id = "1", Name = "Test User" };
    
    // Act
    await repository.Add(user.Id, user);
    var result = await repository.GetById(user.Id);
    
    // Assert
    result.IsSuccess.Should().BeTrue();
    result.Value.Name.Should().Be("Test User");
}

Using EF Core In-Memory Provider

[Fact]
public async Task DbContext_Query_Success()
{
    // Arrange
    var options = new DbContextOptionsBuilder<MyDbContext>()
        .UseInMemoryDatabase(databaseName: "TestDb")
        .Options;
    
    await using var context = new MyDbContext(options);
    context.Users.Add(new User { Name = "Test" });
    await context.SaveChangesAsync();
    
    // Act
    var users = await context.Users.ToListAsync();
    
    // Assert
    users.Should().HaveCount(1);
}

Troubleshooting

Connection Failures

Issue: Cannot connect to SQL Server

Solutions:

  • Verify server is running (Docker container, SQL Server instance)
  • Check firewall rules
  • Verify connection string format
  • For Azure: Ensure managed identity has database permissions

Migration Failures

Issue: dotnet ef migrations fails

Solutions:

  • Ensure design-time factory is registered
  • Check connection string is accessible at design time
  • Verify EF Core tools are installed: dotnet tool install --global dotnet-ef

Azure Identity Issues

Issue: Authentication fails in Azure

Solutions:

  • Verify managed identity is enabled in Azure portal
  • Check SQL database permissions for managed identity
  • Ensure IDENTITY_ENDPOINT environment variable is set

Dependencies

  • Ardalis.Result: For result pattern in repositories
  • Ardalis.Result.FluentValidation: For validation integration
  • Microsoft.EntityFrameworkCore.SqlServer: SQL Server provider for EF Core
  • Microsoft.EntityFrameworkCore.Cosmos: Cosmos DB provider for EF Core
  • Microsoft.EntityFrameworkCore.InMemory: In-memory provider for testing
  • Nabs.Launchpad.Core.SeedData: For seeding database with initial data
  • Nabs.Launchpad.Core.Interfaces: For Orleans grain persistence

Target Framework

  • .NET 10
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
10.0.219 43 1/5/2026
10.0.218 47 1/4/2026
10.0.217 53 1/4/2026 10.0.217 is deprecated because it is no longer maintained.
10.0.216 53 1/4/2026 10.0.216 is deprecated because it is no longer maintained.
10.0.215 60 1/4/2026 10.0.215 is deprecated because it is no longer maintained.
10.0.214 107 1/1/2026 10.0.214 is deprecated because it is no longer maintained.
10.0.213 53 1/1/2026 10.0.213 is deprecated because it is no longer maintained.
10.0.212 53 1/1/2026 10.0.212 is deprecated because it is no longer maintained.
10.0.211 77 12/31/2025 10.0.211 is deprecated because it is no longer maintained.
10.0.210 96 12/30/2025 10.0.210 is deprecated because it is no longer maintained.
10.0.209 93 12/30/2025 10.0.209 is deprecated because it is no longer maintained.
10.0.208 95 12/30/2025 10.0.208 is deprecated because it is no longer maintained.
10.0.207 96 12/29/2025 10.0.207 is deprecated because it is no longer maintained.
10.0.206 97 12/29/2025 10.0.206 is deprecated because it is no longer maintained.
10.0.205 187 12/24/2025 10.0.205 is deprecated because it is no longer maintained.
10.0.204 183 12/21/2025 10.0.204 is deprecated because it is no longer maintained.
10.0.203 278 12/18/2025 10.0.203 is deprecated because it is no longer maintained.
10.0.202 283 12/17/2025 10.0.202 is deprecated because it is no longer maintained.
10.0.200 283 12/17/2025 10.0.200 is deprecated because it is no longer maintained.
10.0.199 432 12/10/2025 10.0.199 is deprecated because it is no longer maintained.
10.0.197 171 12/5/2025 10.0.197 is deprecated because it is no longer maintained.
10.0.196 673 12/3/2025 10.0.196 is deprecated because it is no longer maintained.
10.0.195 686 12/3/2025 10.0.195 is deprecated because it is no longer maintained.
10.0.194 682 12/3/2025 10.0.194 is deprecated because it is no longer maintained.
10.0.193 681 12/2/2025 10.0.193 is deprecated because it is no longer maintained.
10.0.192 181 11/28/2025 10.0.192 is deprecated because it is no longer maintained.
10.0.190 189 11/27/2025 10.0.190 is deprecated because it is no longer maintained.
10.0.189 172 11/23/2025 10.0.189 is deprecated because it is no longer maintained.
10.0.187 169 11/23/2025 10.0.187 is deprecated because it is no longer maintained.
10.0.186 158 11/23/2025 10.0.186 is deprecated because it is no longer maintained.
10.0.184 411 11/20/2025 10.0.184 is deprecated because it is no longer maintained.
10.0.181-rc3 285 11/11/2025 10.0.181-rc3 is deprecated because it is no longer maintained.
10.0.180 298 11/11/2025 10.0.180 is deprecated because it is no longer maintained.
10.0.179-rc2 254 11/11/2025 10.0.179-rc2 is deprecated because it is no longer maintained.
10.0.178-rc2 212 11/10/2025 10.0.178-rc2 is deprecated because it is no longer maintained.
10.0.177-rc2 199 11/10/2025 10.0.177-rc2 is deprecated because it is no longer maintained.
10.0.176-rc2 164 11/6/2025 10.0.176-rc2 is deprecated because it is no longer maintained.
10.0.175-rc2 158 11/6/2025 10.0.175-rc2 is deprecated because it is no longer maintained.
10.0.174-rc2 159 11/5/2025 10.0.174-rc2 is deprecated because it is no longer maintained.
10.0.173-rc2 162 11/3/2025 10.0.173-rc2 is deprecated because it is no longer maintained.
10.0.172-rc2 149 11/2/2025 10.0.172-rc2 is deprecated because it is no longer maintained.
10.0.170-rc2 127 11/1/2025 10.0.170-rc2 is deprecated because it is no longer maintained.
10.0.169-rc2 126 11/1/2025 10.0.169-rc2 is deprecated because it is no longer maintained.
10.0.168-rc2 128 10/31/2025 10.0.168-rc2 is deprecated because it is no longer maintained.
10.0.166-rc2 143 10/31/2025 10.0.166-rc2 is deprecated because it is no longer maintained.
10.0.164-rc2 199 10/28/2025 10.0.164-rc2 is deprecated because it is no longer maintained.
10.0.162-rc2 184 10/24/2025 10.0.162-rc2 is deprecated because it is no longer maintained.
10.0.161 198 10/24/2025 10.0.161 is deprecated because it is no longer maintained.
9.0.151 125 10/17/2025 9.0.151 is deprecated because it is no longer maintained.
9.0.150 201 9/10/2025 9.0.150 is deprecated because it is no longer maintained.
9.0.146 135 8/15/2025 9.0.146 is deprecated because it is no longer maintained.
9.0.145 187 8/11/2025 9.0.145 is deprecated because it is no longer maintained.
9.0.144 198 8/8/2025 9.0.144 is deprecated because it is no longer maintained.
9.0.137 141 7/29/2025 9.0.137 is deprecated because it is no longer maintained.
9.0.136 149 7/29/2025 9.0.136 is deprecated because it is no longer maintained.
9.0.135 157 7/28/2025 9.0.135 is deprecated because it is no longer maintained.
9.0.134 196 7/9/2025 9.0.134 is deprecated because it is no longer maintained.
9.0.133 191 7/9/2025 9.0.133 is deprecated because it is no longer maintained.
9.0.132 193 7/9/2025 9.0.132 is deprecated because it is no longer maintained.
9.0.131 202 7/9/2025 9.0.131 is deprecated because it is no longer maintained.
9.0.130 192 7/7/2025 9.0.130 is deprecated because it is no longer maintained.