Skip to content

Commit c9e74a4

Browse files
committed
Update docs for Multitenancy.EFCore and configuration
Expanded documentation for CleanArchitecture.Extensions.Multitenancy.EFCore, including database-per-tenant setup and connection string resolution. Updated index, authentication, caching, and configuration reference docs to reflect new package, usage patterns, and recommended configuration sections. Added production release checklist to release notes.
1 parent 0aee3ca commit c9e74a4

File tree

7 files changed

+151
-14
lines changed

7 files changed

+151
-14
lines changed

docs/extensions/multitenancy-efcore.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,49 @@ builder.Services.AddCleanArchitectureMultitenancyEfCore(options =>
147147
});
148148
```
149149

150+
## Database-per-tenant setup
151+
152+
```csharp
153+
builder.Services.AddCleanArchitectureMultitenancyEfCore(options =>
154+
{
155+
options.Mode = TenantIsolationMode.DatabasePerTenant;
156+
options.ConnectionStringFormat = "Server=.;Database=Tenant_{0};Trusted_Connection=True;TrustServerCertificate=True;";
157+
});
158+
159+
builder.Services.AddDbContextFactory<ApplicationDbContext>((sp, options) =>
160+
{
161+
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
162+
});
163+
164+
builder.Services.AddTenantDbContextFactory<ApplicationDbContext>();
165+
```
166+
167+
For request-scoped DbContext registration, resolve the tenant connection inside `AddDbContext`:
168+
169+
```csharp
170+
builder.Services.AddDbContext<ApplicationDbContext>((sp, options) =>
171+
{
172+
var currentTenant = sp.GetRequiredService<ICurrentTenant>();
173+
var resolver = sp.GetRequiredService<ITenantConnectionResolver>();
174+
var connectionString = resolver.ResolveConnectionString(currentTenant.TenantInfo);
175+
176+
if (string.IsNullOrWhiteSpace(connectionString))
177+
{
178+
throw new InvalidOperationException("Tenant connection string was not resolved.");
179+
}
180+
181+
options.UseSqlServer(connectionString);
182+
});
183+
```
184+
185+
`ITenantDbContextFactory<TContext>` sets the connection string per tenant for background tasks and migrations. Register your own resolver if connection strings live outside configuration.
186+
150187
## Key components
151188

152189
- `TenantDbContext` base class, `ITenantDbContext`, and `TenantModelCacheKeyFactory`.
153190
- `TenantSaveChangesInterceptor` for tenant enforcement on writes.
154191
- `TenantModelCustomizer` for filters and schema configuration.
155-
- `TenantMigrationRunner<TContext>` for per-tenant migrations.
192+
- `TenantDbContextFactory<TContext>` + `TenantMigrationRunner<TContext>` for per-tenant connections and migrations.
156193

157194
## Related modules
158195

docs/index.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CleanArchitecture.Extensions is a small set of opt-in NuGet packages that plug i
77
- `CleanArchitecture.Extensions.Caching`
88
- `CleanArchitecture.Extensions.Multitenancy`
99
- `CleanArchitecture.Extensions.Multitenancy.AspNetCore`
10+
- `CleanArchitecture.Extensions.Multitenancy.EFCore`
1011

1112
## Quickstart
1213

@@ -42,12 +43,23 @@ dotnet add package CleanArchitecture.Extensions.Multitenancy.AspNetCore
4243
services.AddCleanArchitectureMultitenancyAspNetCore();
4344
```
4445

45-
No other template changes required.
46+
Multitenancy.EFCore:
47+
48+
```powershell
49+
dotnet add package CleanArchitecture.Extensions.Multitenancy.EFCore
50+
```
51+
52+
```csharp
53+
services.AddCleanArchitectureMultitenancyEfCore();
54+
```
55+
56+
No template fork required.
4657

4758
## Where to go next
4859

4960
- Caching docs: [extensions/caching.md](extensions/caching.md)
5061
- Multitenancy docs: [extensions/multitenancy-core.md](extensions/multitenancy-core.md)
5162
- Multitenancy.AspNetCore docs: [extensions/multitenancy-aspnetcore.md](extensions/multitenancy-aspnetcore.md)
63+
- Multitenancy.EFCore docs: [extensions/multitenancy-efcore.md](extensions/multitenancy-efcore.md)
5264
- Extensions catalog: [extensions/index.md](extensions/index.md)
5365
- Roadmap: [roadmap.md](roadmap.md)

docs/recipes/authentication.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ Wire authentication with extension-friendly hooks.
55

66
## Prereqs
77
- Base Clean Architecture template running.
8-
- Auth provider chosen (e.g., JWT, IdentityServer) — placeholder until packages land.
8+
- Auth provider chosen (e.g., JWT, cookies, Identity) using the template defaults.
99

1010
## Steps
11-
1. Add the relevant authentication adapter package (TBD).
12-
2. Configure authentication in Program.cs (or equivalent) with provided helpers.
13-
3. Add middleware/filters for tenant-aware auth if needed.
11+
1. Configure authentication in Program.cs (or equivalent) using the template guidance.
12+
2. If multitenancy is enabled, ensure tenant resolution runs before authorization checks.
13+
3. Add tenant-aware authorization policies if needed.
1414

1515
## Verify
1616
- Hitting a protected endpoint returns 200 with valid token; 401 otherwise.

docs/recipes/caching.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Add caching with clear cache key conventions and opt-in behaviors.
55

66
## Prereqs
77
- Base Clean Architecture template running.
8-
- Choose cache store (in-memory, distributed) — adapters TBD.
8+
- Choose cache store (in-memory default or an `IDistributedCache` implementation for distributed caching).
99

1010
## Steps
1111
1. Add the caching package: `dotnet add package CleanArchitecture.Extensions.Caching`.

docs/reference/configuration.md

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,55 @@
22

33
Configuration keys and environment variables for extensions.
44

5-
- This section expands per extension as packages ship.
6-
- Include defaults, required values, and sample appsettings snippets.
5+
- Use the Options pattern to bind configuration sections to extension options.
6+
- Section names are not enforced; the names below are recommended for consistency.
77

88
## Available references
99

1010
- Multitenancy options: [multitenancy-options.md](multitenancy-options.md)
1111

12-
## Placeholder keys
12+
## Recommended sections
1313

14-
- Extensions:<Name>:Enabled
15-
- Extensions:<Name>:Options (TBD per extension)
14+
- `Extensions:Caching` -> `CachingOptions`
15+
- `Extensions:Caching:QueryBehavior` -> `QueryCachingBehaviorOptions`
16+
- `Extensions:Multitenancy` -> `MultitenancyOptions`
17+
- `Extensions:Multitenancy:AspNetCore` -> `AspNetCoreMultitenancyOptions`
18+
- `Extensions:Multitenancy:EFCore` -> `EfCoreMultitenancyOptions`
19+
20+
## Example
21+
22+
```json
23+
{
24+
"Extensions": {
25+
"Caching": {
26+
"Enabled": true,
27+
"DefaultNamespace": "MyApp",
28+
"QueryBehavior": {
29+
"DefaultTtl": "00:05:00",
30+
"CacheNullValues": false
31+
}
32+
},
33+
"Multitenancy": {
34+
"RequireTenantByDefault": true,
35+
"HeaderNames": [ "X-Tenant-ID" ],
36+
"EFCore": {
37+
"Mode": "SharedDatabase",
38+
"TenantIdPropertyName": "TenantId"
39+
}
40+
}
41+
}
42+
}
43+
```
44+
45+
Bind sections in your host:
46+
47+
```csharp
48+
builder.Services.Configure<CachingOptions>(
49+
builder.Configuration.GetSection("Extensions:Caching"));
50+
builder.Services.Configure<QueryCachingBehaviorOptions>(
51+
builder.Configuration.GetSection("Extensions:Caching:QueryBehavior"));
52+
builder.Services.Configure<MultitenancyOptions>(
53+
builder.Configuration.GetSection("Extensions:Multitenancy"));
54+
builder.Services.Configure<EfCoreMultitenancyOptions>(
55+
builder.Configuration.GetSection("Extensions:Multitenancy:EFCore"));
56+
```

docs/release-notes/index.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,15 @@
22

33
Changelog for CleanArchitecture.Extensions (latest-only for now).
44

5-
- Add a section per release once packages ship.
6-
- Call out breaking changes and migration steps.
5+
## Unreleased
6+
7+
- No public releases yet.
8+
9+
## Production release checklist
10+
11+
- Package metadata validated (`README`, license, icon, SourceLink, symbols).
12+
- Docs updated for all shipped packages (install, config, quickstart).
13+
- CI green: unit tests + basic integration tests for EFCore behaviors.
14+
- Sample integration against the template works without forked changes.
15+
- Breaking changes documented with migrations (if any).
16+
- Security/behavior caveats documented (caching, tenant enforcement, isolation).

src/CleanArchitecture.Extensions.Multitenancy.EFCore/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,43 @@ builder.Services.AddCleanArchitectureMultitenancyEfCore(options =>
144144
});
145145
```
146146

147+
## Database-per-tenant setup
148+
149+
```csharp
150+
builder.Services.AddCleanArchitectureMultitenancyEfCore(options =>
151+
{
152+
options.Mode = TenantIsolationMode.DatabasePerTenant;
153+
options.ConnectionStringFormat = "Server=.;Database=Tenant_{0};Trusted_Connection=True;TrustServerCertificate=True;";
154+
});
155+
156+
builder.Services.AddDbContextFactory<ApplicationDbContext>((sp, options) =>
157+
{
158+
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
159+
});
160+
161+
builder.Services.AddTenantDbContextFactory<ApplicationDbContext>();
162+
```
163+
164+
For request-scoped DbContext registration, resolve the tenant connection inside `AddDbContext`:
165+
166+
```csharp
167+
builder.Services.AddDbContext<ApplicationDbContext>((sp, options) =>
168+
{
169+
var currentTenant = sp.GetRequiredService<ICurrentTenant>();
170+
var resolver = sp.GetRequiredService<ITenantConnectionResolver>();
171+
var connectionString = resolver.ResolveConnectionString(currentTenant.TenantInfo);
172+
173+
if (string.IsNullOrWhiteSpace(connectionString))
174+
{
175+
throw new InvalidOperationException("Tenant connection string was not resolved.");
176+
}
177+
178+
options.UseSqlServer(connectionString);
179+
});
180+
```
181+
182+
`ITenantDbContextFactory<TContext>` switches the connection string per tenant for background tasks and migrations. Register a custom resolver if you want to pull connection strings from a vault or catalog.
183+
147184
## What to expect
148185

149186
- Shared database mode automatically adds a tenant filter (`TenantId`) to all tenant-scoped entities.

0 commit comments

Comments
 (0)