Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/mdsource/postgres-ef.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ include: map-group-ef
include: should-execute-ef


include: suffix-auth-ef


include: last-timestamp-ef
3 changes: 3 additions & 0 deletions docs/mdsource/postgres.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ include: map-group
include: should-execute


include: suffix-auth


### Custom Connection discovery

By default, Delta uses `HttpContext.RequestServices` to discover the NpgsqlConnection and NpgsqlTransaction:
Expand Down
3 changes: 3 additions & 0 deletions docs/mdsource/sqlserver-ef.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ include: map-group-ef
include: should-execute-ef


include: suffix-auth-ef


include: last-timestamp-ef


Expand Down
3 changes: 3 additions & 0 deletions docs/mdsource/sqlserver.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ include: map-group
include: should-execute


include: suffix-auth


### Custom Connection discovery

By default, Delta uses `HttpContext.RequestServices` to discover the SqlConnection and SqlTransaction:
Expand Down
16 changes: 16 additions & 0 deletions docs/mdsource/suffix-auth-ef.include.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
### Suffix and Authentication

When using a `suffix` callback that accesses `HttpContext.User` claims, authentication middleware **must** run before `UseDelta`. If `UseDelta` runs first, the User claims won't be populated yet, and all users will get the same cache key.

Delta automatically detects this misconfiguration and throws an `InvalidOperationException` with a helpful message if:
- A `suffix` callback is provided
- The user is not authenticated (`context.User.Identity?.IsAuthenticated != true`)

snippet: SuffixWithAuthEF


### AllowAnonymous

For endpoints that intentionally allow anonymous access but still want to use a suffix for cache differentiation (e.g., based on request headers rather than user claims), use `allowAnonymous: true`:

snippet: AllowAnonymousEF
16 changes: 16 additions & 0 deletions docs/mdsource/suffix-auth.include.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
### Suffix and Authentication

When using a `suffix` callback that accesses `HttpContext.User` claims, authentication middleware **must** run before `UseDelta`. If `UseDelta` runs first, the User claims won't be populated yet, and all users will get the same cache key.

Delta automatically detects this misconfiguration and throws an `InvalidOperationException` with a helpful message if:
- A `suffix` callback is provided
- The user is not authenticated (`context.User.Identity?.IsAuthenticated != true`)

snippet: SuffixWithAuth


### AllowAnonymous

For endpoints that intentionally allow anonymous access but still want to use a suffix for cache differentiation (e.g., based on request headers rather than user claims), use `allowAnonymous: true`:

snippet: AllowAnonymous
57 changes: 54 additions & 3 deletions docs/postgres-ef.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,58 @@ app.UseDelta<SampleDbContext>(
return path.Contains("match");
});
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L16-L26' title='Snippet source file'>snippet source</a> | <a href='#snippet-ShouldExecuteEF' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Delta.EFTests/Usage.cs#L18-L28' title='Snippet source file'>snippet source</a> | <a href='#snippet-ShouldExecuteEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->


### Suffix and Authentication<!-- include: suffix-auth-ef. path: /docs/mdsource/suffix-auth-ef.include.md -->

When using a `suffix` callback that accesses `HttpContext.User` claims, authentication middleware **must** run before `UseDelta`. If `UseDelta` runs first, the User claims won't be populated yet, and all users will get the same cache key.

Delta automatically detects this misconfiguration and throws an `InvalidOperationException` with a helpful message if:
- A `suffix` callback is provided
- The user is not authenticated (`context.User.Identity?.IsAuthenticated != true`)

<!-- snippet: SuffixWithAuthEF -->
<a id='snippet-SuffixWithAuthEF'></a>
```cs
var app = builder.Build();

// Authentication middleware must run before UseDelta
// so that User claims are available to the suffix callback
app.UseAuthentication();
app.UseAuthorization();

app.UseDelta<SampleDbContext>(
suffix: httpContext =>
{
// Access user claims to create per-user cache keys
var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var tenantId = httpContext.User.FindFirst("TenantId")?.Value;
return $"{userId}-{tenantId}";
});
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L33-L51' title='Snippet source file'>snippet source</a> | <a href='#snippet-SuffixWithAuthEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


### AllowAnonymous

For endpoints that intentionally allow anonymous access but still want to use a suffix for cache differentiation (e.g., based on request headers rather than user claims), use `allowAnonymous: true`:

<!-- snippet: AllowAnonymousEF -->
<a id='snippet-AllowAnonymousEF'></a>
```cs
var app = builder.Build();

// For endpoints that intentionally allow anonymous access
// but still want a suffix for cache differentiation
app.UseDelta<SampleDbContext>(
suffix: httpContext => httpContext.Request.Headers["X-Client-Version"].ToString(),
allowAnonymous: true);
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L56-L66' title='Snippet source file'>snippet source</a> | <a href='#snippet-AllowAnonymousEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->

Expand All @@ -167,7 +218,7 @@ It can be called on a DbContext:
```cs
var timeStamp = await dbContext.GetLastTimeStamp();
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L41-L45' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampEF' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Delta.EFTests/Usage.cs#L81-L85' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Or a DbConnection:
Expand All @@ -177,6 +228,6 @@ Or a DbConnection:
```cs
var timeStamp = await connection.GetLastTimeStamp();
```
<sup><a href='/src/DeltaTests/Usage.cs#L188-L192' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampConnection' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L226-L230' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampConnection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->
57 changes: 54 additions & 3 deletions docs/postgres.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,57 @@ app.UseDelta(
<!-- endInclude -->


### Suffix and Authentication<!-- include: suffix-auth. path: /docs/mdsource/suffix-auth.include.md -->

When using a `suffix` callback that accesses `HttpContext.User` claims, authentication middleware **must** run before `UseDelta`. If `UseDelta` runs first, the User claims won't be populated yet, and all users will get the same cache key.

Delta automatically detects this misconfiguration and throws an `InvalidOperationException` with a helpful message if:
- A `suffix` callback is provided
- The user is not authenticated (`context.User.Identity?.IsAuthenticated != true`)

<!-- snippet: SuffixWithAuth -->
<a id='snippet-SuffixWithAuth'></a>
```cs
var app = builder.Build();

// Authentication middleware must run before UseDelta
// so that User claims are available to the suffix callback
app.UseAuthentication();
app.UseAuthorization();

app.UseDelta(
suffix: httpContext =>
{
// Access user claims to create per-user cache keys
var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var tenantId = httpContext.User.FindFirst("TenantId")?.Value;
return $"{userId}-{tenantId}";
});
```
<sup><a href='/src/DeltaTests/Usage.cs#L34-L52' title='Snippet source file'>snippet source</a> | <a href='#snippet-SuffixWithAuth' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


### AllowAnonymous

For endpoints that intentionally allow anonymous access but still want to use a suffix for cache differentiation (e.g., based on request headers rather than user claims), use `allowAnonymous: true`:

<!-- snippet: AllowAnonymous -->
<a id='snippet-AllowAnonymous'></a>
```cs
var app = builder.Build();

// For endpoints that intentionally allow anonymous access
// but still want a suffix for cache differentiation
app.UseDelta(
suffix: httpContext => httpContext.Request.Headers["X-Client-Version"].ToString(),
allowAnonymous: true);
```
<sup><a href='/src/DeltaTests/Usage.cs#L57-L67' title='Snippet source file'>snippet source</a> | <a href='#snippet-AllowAnonymous' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->


### Custom Connection discovery

By default, Delta uses `HttpContext.RequestServices` to discover the NpgsqlConnection and NpgsqlTransaction:
Expand Down Expand Up @@ -162,7 +213,7 @@ application.UseDelta(
getConnection: httpContext =>
httpContext.RequestServices.GetRequiredService<NpgsqlConnection>());
```
<sup><a href='/src/DeltaTests/Usage.cs#L337-L344' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnectionPostgres' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L375-L382' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnectionPostgres' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

To use custom connection and transaction discovery:
Expand All @@ -180,7 +231,7 @@ application.UseDelta(
return new(connection, transaction);
});
```
<sup><a href='/src/DeltaTests/Usage.cs#L365-L377' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnectionAndTransactionPostgres' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L403-L415' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnectionAndTransactionPostgres' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand All @@ -193,6 +244,6 @@ application.UseDelta(
```cs
var timeStamp = await connection.GetLastTimeStamp();
```
<sup><a href='/src/DeltaTests/Usage.cs#L188-L192' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampConnection' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L226-L230' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampConnection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->
69 changes: 60 additions & 9 deletions docs/sqlserver-ef.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,58 @@ app.UseDelta<SampleDbContext>(
return path.Contains("match");
});
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L16-L26' title='Snippet source file'>snippet source</a> | <a href='#snippet-ShouldExecuteEF' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Delta.EFTests/Usage.cs#L18-L28' title='Snippet source file'>snippet source</a> | <a href='#snippet-ShouldExecuteEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->


### Suffix and Authentication<!-- include: suffix-auth-ef. path: /docs/mdsource/suffix-auth-ef.include.md -->

When using a `suffix` callback that accesses `HttpContext.User` claims, authentication middleware **must** run before `UseDelta`. If `UseDelta` runs first, the User claims won't be populated yet, and all users will get the same cache key.

Delta automatically detects this misconfiguration and throws an `InvalidOperationException` with a helpful message if:
- A `suffix` callback is provided
- The user is not authenticated (`context.User.Identity?.IsAuthenticated != true`)

<!-- snippet: SuffixWithAuthEF -->
<a id='snippet-SuffixWithAuthEF'></a>
```cs
var app = builder.Build();

// Authentication middleware must run before UseDelta
// so that User claims are available to the suffix callback
app.UseAuthentication();
app.UseAuthorization();

app.UseDelta<SampleDbContext>(
suffix: httpContext =>
{
// Access user claims to create per-user cache keys
var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var tenantId = httpContext.User.FindFirst("TenantId")?.Value;
return $"{userId}-{tenantId}";
});
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L33-L51' title='Snippet source file'>snippet source</a> | <a href='#snippet-SuffixWithAuthEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


### AllowAnonymous

For endpoints that intentionally allow anonymous access but still want to use a suffix for cache differentiation (e.g., based on request headers rather than user claims), use `allowAnonymous: true`:

<!-- snippet: AllowAnonymousEF -->
<a id='snippet-AllowAnonymousEF'></a>
```cs
var app = builder.Build();

// For endpoints that intentionally allow anonymous access
// but still want a suffix for cache differentiation
app.UseDelta<SampleDbContext>(
suffix: httpContext => httpContext.Request.Headers["X-Client-Version"].ToString(),
allowAnonymous: true);
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L56-L66' title='Snippet source file'>snippet source</a> | <a href='#snippet-AllowAnonymousEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->

Expand All @@ -220,7 +271,7 @@ It can be called on a DbContext:
```cs
var timeStamp = await dbContext.GetLastTimeStamp();
```
<sup><a href='/src/Delta.EFTests/Usage.cs#L41-L45' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampEF' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Delta.EFTests/Usage.cs#L81-L85' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampEF' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Or a DbConnection:
Expand All @@ -230,7 +281,7 @@ Or a DbConnection:
```cs
var timeStamp = await connection.GetLastTimeStamp();
```
<sup><a href='/src/DeltaTests/Usage.cs#L188-L192' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampConnection' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L226-L230' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampConnection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
<!-- endInclude -->

Expand All @@ -255,7 +306,7 @@ foreach (var db in trackedDatabases)
Trace.WriteLine(db);
}
```
<sup><a href='/src/DeltaTests/Usage.cs#L204-L212' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetDatabasesWithTracking' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L242-L250' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetDatabasesWithTracking' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -285,7 +336,7 @@ foreach (var db in trackedTables)
Trace.WriteLine(db);
}
```
<sup><a href='/src/DeltaTests/Usage.cs#L230-L238' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetTrackedTables' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L268-L276' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetTrackedTables' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand All @@ -310,7 +361,7 @@ Determine if change tracking is enabled for a database.
```cs
var isTrackingEnabled = await sqlConnection.IsTrackingEnabled();
```
<sup><a href='/src/DeltaTests/Usage.cs#L315-L319' title='Snippet source file'>snippet source</a> | <a href='#snippet-IsTrackingEnabled' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L353-L357' title='Snippet source file'>snippet source</a> | <a href='#snippet-IsTrackingEnabled' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand All @@ -337,7 +388,7 @@ Enable change tracking for a database.
```cs
await sqlConnection.EnableTracking();
```
<sup><a href='/src/DeltaTests/Usage.cs#L309-L313' title='Snippet source file'>snippet source</a> | <a href='#snippet-EnableTracking' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L347-L351' title='Snippet source file'>snippet source</a> | <a href='#snippet-EnableTracking' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -365,7 +416,7 @@ Disable change tracking for a database and all tables within that database.
```cs
await sqlConnection.DisableTracking();
```
<sup><a href='/src/DeltaTests/Usage.cs#L294-L298' title='Snippet source file'>snippet source</a> | <a href='#snippet-DisableTracking' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L332-L336' title='Snippet source file'>snippet source</a> | <a href='#snippet-DisableTracking' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -402,7 +453,7 @@ Enables change tracking for all tables listed, and disables change tracking for
```cs
await sqlConnection.SetTrackedTables(["Companies"]);
```
<sup><a href='/src/DeltaTests/Usage.cs#L224-L228' title='Snippet source file'>snippet source</a> | <a href='#snippet-SetTrackedTables' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L262-L266' title='Snippet source file'>snippet source</a> | <a href='#snippet-SetTrackedTables' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down
Loading