Omniport 1.0.0

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

<p align="center"> <img src="omniport_logo.svg" alt="Omniport logo" width="180" /> </p>

<h1 align="center">Omniport</h1>

<p align="center"> A Standardized Provider Abstraction Library (SPAL) for swapping SQL Server, PostgreSQL, Cosmos DB, and MongoDB storage backends via configuration only. </p>


What is Omniport?

Omniport gives every microservice a single shared contract, IStorageBrokerProvider, that each database provider implements. A microservice keeps its own StorageBroker — it still owns its entity model, OnModelCreating, and migrations — but delegates all provider-specific configuration (which EF Core provider to use, connection strings, dialect quirks) to whichever IStorageBrokerProvider is injected.

The result: swapping a microservice's database backend is a config value change (Storage:Backend) plus a connection string — no recompilation of business logic.

This follows The Standard, Chapter 1.8 — Standardized Provider Abstraction Libraries (SPAL).

Packages

Package Purpose
Omniport The shared contract (IStorageBrokerProvider, TargetServerType). Every other package depends on it. Kept small and stable — a breaking change here is a breaking change everywhere.
Omniport.SqlServer SQL Server provider (Microsoft.EntityFrameworkCore.SqlServer).
Omniport.PostgreSql PostgreSQL provider (Npgsql.EntityFrameworkCore.PostgreSQL), plus a DateTimeOffsetTruncationInterceptor that handles the Postgres timestamptz precision mismatch against .NET's DateTimeOffset.
Omniport.Cosmos Cosmos DB provider (Microsoft.EntityFrameworkCore.Cosmos), with local-emulator support via CosmosStorageBrokerOptions.
Omniport.MongoDb MongoDB provider (MongoDB.EntityFrameworkCore), with local-instance support via MongoDbStorageBrokerOptions.

Only reference the providers a given microservice actually needs — there's no requirement to pull in all four if a service will only ever run against one or two backends.

Install

dotnet add package Omniport
dotnet add package Omniport.SqlServer     # or PostgreSql / Cosmos / MongoDb

Pin exact versions in consuming services — don't float:

<PackageReference Include="Omniport" Version="1.0.0" />
<PackageReference Include="Omniport.SqlServer" Version="1.0.0" />

Usage

1. StorageBroker — delegate provider config to Omniport

public partial class StorageBroker : EFxceptionsContext, IStorageBroker
{
    private readonly IStorageBrokerProvider storageBrokerProvider;

    public StorageBroker(IStorageBrokerProvider storageBrokerProvider)
    {
        this.storageBrokerProvider = storageBrokerProvider;

        // Cosmos and MongoDB are schemaless — no relational migration history.
        if (storageBrokerProvider is CosmosStorageBrokerProvider
            || storageBrokerProvider is MongoDbStorageBrokerProvider)
        {
            this.Database.EnsureCreated();
        }
        else
        {
            this.Database.Migrate();
        }
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
        this.storageBrokerProvider.Configure(optionsBuilder);

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // this service's own entity configuration goes here first
        this.storageBrokerProvider.ConfigureModel(modelBuilder);
    }

    protected override void ConfigureConventions(
        ModelConfigurationBuilder configurationBuilder) =>
            this.storageBrokerProvider.ConfigureConventions(configurationBuilder);
}

2. Program.cs — pick a provider from config

string backend = builder.Configuration["Storage:Backend"]; // "SqlServer" | "Postgres" | "Cosmos" | "MongoDb"
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
string migrationsAssembly = $"YourService.{backend}";

builder.Services.AddSingleton<IStorageBrokerProvider>(_ => backend switch
{
    "SqlServer" => new SqlServerStorageBrokerProvider(connectionString, migrationsAssembly),
    "Postgres"  => new PostgreSqlStorageBrokerProvider(connectionString, migrationsAssembly),
    "Cosmos"    => new CosmosStorageBrokerProvider(new CosmosStorageBrokerOptions
    {
        ConnectionString = connectionString,
        DatabaseName = builder.Configuration["Storage:Cosmos:DatabaseName"],
        TargetServerType = builder.Configuration.GetValue<bool>("Storage:UseLocalEmulator")
            ? TargetServerType.Local
            : TargetServerType.Remote
    }),
    "MongoDb"   => new MongoDbStorageBrokerProvider(new MongoDbStorageBrokerOptions
    {
        ConnectionString = connectionString,
        DatabaseName = builder.Configuration["Storage:MongoDb:DatabaseName"],
        TargetServerType = builder.Configuration.GetValue<bool>("Storage:UseLocalEmulator")
            ? TargetServerType.Local
            : TargetServerType.Remote
    }),
    _ => throw new NotSupportedException($"Unsupported storage backend: {backend}")
});

builder.Services.AddDbContext<StorageBroker>();

3. appsettings.json

{
  "Storage": {
    "Backend": "SqlServer",
    "UseLocalEmulator": false,
    "Cosmos": { "DatabaseName": "YourServiceDB" },
    "MongoDb": { "DatabaseName": "YourServiceDB" }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=...;Database=...;"
  }
}

Switching backends is now: change Storage:Backend + ConnectionStrings:DefaultConnection. Nothing else in the microservice changes.

4. Migrations (SQL Server / PostgreSQL only)

Cosmos and MongoDB are schemaless — they don't support dotnet ef migrations. Provisioning happens via Database.EnsureCreated() at startup instead (see the StorageBroker snippet above).

For SQL Server and PostgreSQL, each relational provider project in the consuming microservice's own repo needs a design-time factory so dotnet ef can construct a StorageBroker without full app startup:

internal class SqlServerContextFactory : IDesignTimeDbContextFactory<StorageBroker>
{
    public StorageBroker CreateDbContext(string[] args)
    {
        string connectionString =
            Environment.GetEnvironmentVariable("SQLSERVER_CONNECTION_STRING")
                ?? "Server=localhost;Database=YourServiceDB;Trusted_Connection=True;TrustServerCertificate=True";

        return new StorageBroker(
            new SqlServerStorageBrokerProvider(connectionString, "YourService.SqlServer"));
    }
}
dotnet ef migrations add InitialCreate --project YourService.SqlServer --context StorageBroker --output-dir Migrations
dotnet ef database update --project YourService.SqlServer --context StorageBroker

Give the same migration the same name across providers (InitialCreate, AddUsageLimitIndex, ...) even though they live in separate folders, so it's obvious they correspond to the same logical schema change. Run dotnet ef database update for every provider on every deploy, not just the currently-active one, so a future Storage:Backend flip doesn't hit a stale/uninitialized database.

5. Provider-specific notes

PostgreSQLConfigureConventions maps DateTimeOffset to timestamptz at microsecond precision, and DateTimeOffsetTruncationInterceptor truncates outgoing DateTimeOffset parameters to match, since Postgres timestamptz doesn't carry the full precision .NET's DateTimeOffset does.

Cosmos DB — entities need a partition key configured per-entity in the microservice's OnModelCreating, since only the microservice knows its entity shapes: modelBuilder.Entity<T>().HasPartitionKey(x => x.SomeProperty).

MongoDBMongoDB.EntityFrameworkCore is newer and less feature-complete than the relational/Cosmos EF Core providers (no migrations, limited LINQ operator support, different owned-entity mapping). Confirm current capabilities against the official docs before relying on advanced query patterns. If it's too limited for a given service, that service can bypass DbContext and use MongoDB.Driver directly for the affected operations while still using MongoDbStorageBrokerProvider elsewhere — that's a microservice-level decision and doesn't change this library.

Additional MongoDB specifics to configure in the microservice's OnModelCreating:

// MongoDB has no native DateTimeOffset — store as UTC DateTime and convert.
modelBuilder.Entity<YourEntity>()
    .Property(e => e.CreatedAt)
    .HasConversion(v => v.UtcDateTime, v => new DateTimeOffset(v, TimeSpan.Zero));

// MongoDB documents use `_id` as the primary key field by convention.
modelBuilder.Entity<YourEntity>()
    .Property(e => e.Id)
    .HasElementName("_id");

6. External Mockability (Cloud-Foreign compliance)

For Acceptance Testing / Airplane-Mode runs, each provider supports a local target via TargetServerType.Local:

  • SQL Server / PostgreSQL — point the connection string at a Testcontainers-managed container.
  • Cosmos DB — run the Azure Cosmos DB Emulator and set TargetServerType.Local, which routes to CosmosStorageBrokerOptions.LocalEmulatorEndpoint / LocalEmulatorKey.
  • MongoDB — run the official mongo Docker image and set TargetServerType.Local, which routes to MongoDbStorageBrokerOptions.LocalConnectionString (mongodb://localhost:27017 by default).

A consuming service's acceptance test suite can drive provider selection from environment variables (a PROVIDER / CONNECTION_STRING pair read into a ClientBroker) so a CI matrix runs the whole suite once per backend without any code duplication — see the pattern description in this repo's build spec for the full example.

Versioning

Semver, strictly enforced:

  • PATCH (1.0.x) — bug fixes only, no interface changes.
  • MINOR (1.x.0) — new capability, fully backward compatible (e.g. an optional overload).
  • MAJOR (x.0.0) — any change to IStorageBrokerProvider's signature, or any change to a provider constructor's parameters. Every consuming microservice must bump to this deliberately after testing — never auto-float major versions.

Contributing

This repo follows The Standard. See .agents/skills/the-standard-* for the coding, testing, and contribution-practice rules enforced here.

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 (2)

Showing the top 2 NuGet packages that depend on Omniport:

Package Downloads
Omniport.PostgreSql

PostgreSQL storage broker provider for Omniport.

Omniport.SqlServer

SQL Server storage broker provider for Omniport.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 105 8/11/2026