PrimusSaaS.MultiTenancy.Dapper
1.3.0
dotnet add package PrimusSaaS.MultiTenancy.Dapper --version 1.3.0
NuGet\Install-Package PrimusSaaS.MultiTenancy.Dapper -Version 1.3.0
<PackageReference Include="PrimusSaaS.MultiTenancy.Dapper" Version="1.3.0" />
<PackageVersion Include="PrimusSaaS.MultiTenancy.Dapper" Version="1.3.0" />
<PackageReference Include="PrimusSaaS.MultiTenancy.Dapper" />
paket add PrimusSaaS.MultiTenancy.Dapper --version 1.3.0
#r "nuget: PrimusSaaS.MultiTenancy.Dapper, 1.3.0"
#:package PrimusSaaS.MultiTenancy.Dapper@1.3.0
#addin nuget:?package=PrimusSaaS.MultiTenancy.Dapper&version=1.3.0
#tool nuget:?package=PrimusSaaS.MultiTenancy.Dapper&version=1.3.0
PrimusSaaS.MultiTenancy.Dapper
Dapper + Npgsql storage adapter for PrimusSaaS.MultiTenancy. Implements ITenantStore, ITenantMembershipStore, and ITenantRlsInitializer using raw SQL — no Entity Framework dependency.
Compatible with .NET 8, 9, and 10.
Quick Start
// Program.cs
builder.Services
.AddPrimusMultiTenancy(opts =>
builder.Configuration.GetSection("PrimusMultiTenancy").Bind(opts))
.AddMultiTenancyDapper(
new NpgsqlTenantDbConnectionFactory(
builder.Configuration.GetConnectionString("TenantDb")!));
PostgreSQL Schema
Run this DDL once per database before starting the application:
-- Tenants table
CREATE TABLE IF NOT EXISTS primus_tenants (
tenant_id TEXT PRIMARY KEY,
display_name TEXT,
slug TEXT UNIQUE,
status INT NOT NULL DEFAULT 0,
metadata JSONB,
created_at_utc TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at_utc TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_primus_tenants_slug ON primus_tenants (slug);
CREATE INDEX IF NOT EXISTS idx_primus_tenants_status ON primus_tenants (status);
-- Tenant memberships table
CREATE TABLE IF NOT EXISTS primus_tenant_memberships (
tenant_id TEXT NOT NULL REFERENCES primus_tenants(tenant_id) ON DELETE CASCADE,
principal_id TEXT NOT NULL,
roles JSONB,
is_active BOOL NOT NULL DEFAULT TRUE,
expires_at_utc TIMESTAMPTZ,
PRIMARY KEY (tenant_id, principal_id)
);
CREATE INDEX IF NOT EXISTS idx_primus_memberships_principal ON primus_tenant_memberships (principal_id);
Row-Level Security (RLS)
Enable RLS on tenant-owned tables so each database session can only see its own tenant's rows:
-- Enable RLS on tenants table (optional — tenant_id is the PK so cross-access is explicit)
ALTER TABLE primus_tenants ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_policy ON primus_tenants
AS PERMISSIVE
FOR ALL
USING (tenant_id = current_setting('app.current_tenant', TRUE));
-- Enable RLS on memberships
ALTER TABLE primus_tenant_memberships ENABLE ROW LEVEL SECURITY;
CREATE POLICY membership_tenant_isolation ON primus_tenant_memberships
AS PERMISSIVE
FOR ALL
USING (tenant_id = current_setting('app.current_tenant', TRUE));
Set the session variable before executing queries:
// Inject ITenantRlsInitializer and call it inside a transaction
await using var conn = dataSource.CreateConnection();
await conn.OpenAsync();
await using var tx = await conn.BeginTransactionAsync();
await rlsInitializer.InitializeAsync(conn, tenantId); // SET LOCAL app.current_tenant = '...'
// ... your queries run here under RLS
await tx.CommitAsync();
TenantStatus Values
| Value | Int |
|---|---|
| Active | 0 |
| Suspended | 1 |
| Deleted | 2 |
Environment Variables
ConnectionStrings__TenantDb=Host=localhost;Database=primus;Username=app;Password=secret;
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- Dapper (>= 2.1.79)
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Npgsql (>= 9.0.4)
- PrimusSaaS.MultiTenancy (>= 1.3.0)
-
net8.0
- Dapper (>= 2.1.79)
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Npgsql (>= 9.0.4)
- PrimusSaaS.MultiTenancy (>= 1.3.0)
-
net9.0
- Dapper (>= 2.1.79)
- Microsoft.Data.SqlClient (>= 5.2.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Npgsql (>= 9.0.4)
- PrimusSaaS.MultiTenancy (>= 1.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.0.0: Initial release — Dapper + Npgsql adapter for MultiTenancy with PostgreSQL RLS support.