Skip to content

Commit 5a83c74

Browse files
committed
updated claude.md
1 parent 9168289 commit 5a83c74

File tree

1 file changed

+284
-28
lines changed

1 file changed

+284
-28
lines changed

CLAUDE.md

Lines changed: 284 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,40 +22,164 @@ dotnet test tests/Thinktecture.EntityFrameworkCore.SqlServer.Tests/ -c Release
2222

2323
## Architecture Overview
2424

25-
This repository extends Entity Framework Core with performance enhancements and testing utilities across multiple database providers.
25+
This repository extends Entity Framework Core with performance enhancements and testing utilities across multiple database providers. The architecture follows a layered approach with shared abstractions and provider-specific implementations.
2626

2727
### Core Package Structure
2828

2929
**Runtime Packages (`src/`)**:
30-
- `Thinktecture.EntityFrameworkCore.Relational` - Base abstractions and shared functionality
31-
- `Thinktecture.EntityFrameworkCore.BulkOperations` - Multi-provider bulk operations
32-
- `Thinktecture.EntityFrameworkCore.SqlServer` - SQL Server specific features
33-
- `Thinktecture.EntityFrameworkCore.Sqlite` - SQLite specific features
34-
- `Thinktecture.EntityFrameworkCore.Testing` - Base testing infrastructure
35-
- `Thinktecture.EntityFrameworkCore.SqlServer.Testing` - SQL Server test helpers
36-
- `Thinktecture.EntityFrameworkCore.Sqlite.Testing` - SQLite test helpers
30+
31+
1. **`Thinktecture.EntityFrameworkCore.Relational`** - Foundation layer
32+
- Base abstractions for all relational providers
33+
- Window functions (`RowNumber`, `PartitionBy`, `OrderBy`)
34+
- LEFT JOIN support via `LeftJoin()` extension methods
35+
- Table hints abstraction (`ITableHint`, `WithTableHints()`)
36+
- Nested transaction support (`NestedRelationalTransactionManager`)
37+
- Entity data readers (`IEntityDataReader`, `EntityDataReader`)
38+
- Default schema handling (`IDbDefaultSchema`, `DefaultSchemaModelCustomizer`)
39+
- Tenant database provider infrastructure (`ITenantDatabaseProvider`)
40+
- Query translation extensions and SQL expression types
41+
42+
2. **`Thinktecture.EntityFrameworkCore.BulkOperations`** - Bulk operations layer
43+
- Provider-agnostic interfaces:
44+
- `IBulkInsertExecutor` - Bulk insert operations
45+
- `IBulkUpdateExecutor` - Bulk update operations
46+
- `IBulkInsertOrUpdateExecutor` - Bulk upsert (MERGE) operations
47+
- `ITruncateTableExecutor` - Table truncation
48+
- Temp table abstractions:
49+
- `ITempTableCreator` - Creates temp tables with lifecycle management
50+
- `ITempTableReference` - Represents a temp table instance
51+
- `ITempTableQuery<T>` - Queryable wrapper with automatic cleanup
52+
- `ITempTableBulkInsertExecutor` - Bulk insert into temp tables
53+
- Property selection strategies:
54+
- `IEntityPropertiesProvider` - Base interface for property filtering
55+
- `IncludingEntityPropertiesProvider` - Include specific properties
56+
- `ExcludingEntityPropertiesProvider` - Exclude specific properties
57+
- Temp table name management with suffix leasing for concurrency
58+
- Collection parameter support (`ICollectionParameterFactory`, `ScalarCollectionParameter`)
59+
60+
3. **`Thinktecture.EntityFrameworkCore.SqlServer`** - SQL Server implementation
61+
- Bulk operations via `SqlBulkCopy` (inserts) and MERGE statements (updates/upserts)
62+
- SQL Server-specific options:
63+
- `SqlServerBulkInsertOptions` - Bulk insert configuration
64+
- `SqlServerBulkUpdateOptions` - Bulk update configuration
65+
- `SqlServerBulkInsertOrUpdateOptions` - Upsert configuration
66+
- `SqlServerTempTableBulkInsertOptions` - Temp table bulk insert options
67+
- Table hints: `SqlServerTableHint` enum with values like `NoLock`, `ReadPast`, `UpdLock`, etc.
68+
- Temp table support with SQL Server-specific features
69+
- JSON collection parameters for passing complex data structures
70+
- Window function implementations
71+
- Migration customizations (`ThinktectureSqlServerMigrationsSqlGenerator`)
72+
- Context factories for managing connections and transactions
73+
- Query translation and SQL generation customizations
74+
75+
4. **`Thinktecture.EntityFrameworkCore.Sqlite`** - SQLite implementation
76+
- Simplified bulk operations using INSERT statements
77+
- SQLite-specific options:
78+
- `SqliteBulkInsertOptions` - Bulk insert configuration
79+
- `SqliteBulkUpdateOptions` - Bulk update configuration
80+
- `SqliteBulkInsertOrUpdateOptions` - Upsert configuration
81+
- `SqliteAutoIncrementBehavior` - Control auto-increment handling
82+
- Temp table support adapted for SQLite limitations
83+
- Command builders for generating SQLite-compatible SQL
84+
- Query translation customizations for SQLite dialect
85+
86+
5. **`Thinktecture.EntityFrameworkCore.Testing`** - Testing infrastructure base
87+
- Test context providers (`ITestDbContextProvider`, `TestDbContextProviderBuilder`)
88+
- Migration execution strategies:
89+
- `IMigrationExecutionStrategy` - Base interface
90+
- `MigrationExecutionStrategy` - Runs pending migrations
91+
- `EnsureCreatedMigrationExecutionStrategy` - Uses EnsureCreated
92+
- `NoMigrationExecutionStrategy` - No migration execution
93+
- Command capturing (`CommandCapturingInterceptor`) for SQL verification
94+
- Per-context model cache (`CachePerContextModelCacheKeyFactory`)
95+
- Logging infrastructure (`SubLoggerFactory`, `TestingLoggingOptions`)
96+
- Async enumerable helpers for testing
97+
98+
6. **`Thinktecture.EntityFrameworkCore.SqlServer.Testing`** - SQL Server test helpers
99+
- `SqlServerDbContextIntegrationTests` - Base class for SQL Server integration tests
100+
- `SqlServerTestDbContextProvider` - SQL Server-specific test context provider
101+
- Test isolation via lock tables (`SqlServerLockTableOptions`)
102+
- Shared connection management for test performance
103+
104+
7. **`Thinktecture.EntityFrameworkCore.Sqlite.Testing`** - SQLite test helpers
105+
- `SqliteDbContextIntegrationTests<T>` - Base class for SQLite integration tests
106+
- `SqliteTestDbContextProvider` - SQLite-specific test context provider
107+
- In-memory database support for fast tests
108+
- Context provider factory for test fixtures
37109

38110
**Test Structure (`tests/`)**:
39111
- Tests mirror the `src/` structure with `.Tests` suffix
40112
- `TestHelpers` package provides shared test utilities
113+
- Integration tests use provider-specific base classes
41114

42115
### Key Architectural Patterns
43116

44117
**Bulk Operations Architecture**:
45-
- Provider-agnostic interfaces (`IBulkInsertExecutor`, `IBulkUpdateExecutor`, etc.)
46-
- SQL Server implementation uses `SqlBulkCopy` for inserts and MERGE statements for updates
47-
- Context factories manage connection lifecycle and transaction handling
48-
- Strongly-typed options classes inherit from base interfaces
118+
- **Provider-agnostic design**: Interfaces in `BulkOperations` package define contracts
119+
- **Provider-specific implementations**: SQL Server uses `SqlBulkCopy` for fast inserts; SQLite uses batched INSERT statements
120+
- **Context factories**: Manage connection lifecycle, transactions, and resource cleanup
121+
- **Options pattern**: Strongly-typed options classes (`SqlServerBulkInsertOptions`, etc.) configure behavior
122+
- **Property providers**: Control which entity properties participate in bulk operations
123+
- **Owned entity support**: Special handling for EF Core owned entities with complex object graphs
49124

50125
**Temp Tables Architecture**:
51-
- `ITempTableCreator` creates temp tables with `ITempTableReference` for lifecycle management
52-
- `ITempTableQuery<T>` wraps `IQueryable<T>` with automatic cleanup via `IAsyncDisposable`
53-
- Integration with bulk operations via `ITempTableBulkInsertExecutor`
126+
- **Lifecycle management**: `ITempTableCreator` creates tables; `ITempTableReference` handles cleanup
127+
- **Queryable integration**: `ITempTableQuery<T>` wraps `IQueryable<T>` with `IAsyncDisposable` for automatic cleanup
128+
- **Bulk insert integration**: `ITempTableBulkInsertExecutor` enables fast population of temp tables
129+
- **Name management**: Suffix-based naming with leasing mechanism prevents conflicts in concurrent scenarios
130+
- **Primary key strategies**: Multiple providers (`ConfiguredPrimaryKeyPropertiesProvider`, `AdaptiveEntityTypeConfigurationPrimaryKeyPropertiesProvider`) control PK creation
131+
- **Caching**: Statement caching (`TempTableStatementCache`) improves performance for repeated operations
132+
- **Provider-specific features**: SQL Server supports persistent temp tables; SQLite uses temporary tables
133+
134+
**Window Functions Architecture**:
135+
- **Fluent API**: `EF.Functions.RowNumber()`, `EF.Functions.PartitionBy()`, `EF.Functions.OrderBy()`
136+
- **Server-side evaluation**: Translates to native SQL window functions
137+
- **Provider support**: Implemented in both SQL Server and SQLite (via query translators)
138+
- **Expression-based**: Uses custom SQL expressions (`WindowFunctionExpression`, `WindowFunctionPartitionByExpression`)
139+
140+
**LEFT JOIN Support**:
141+
- **Extension methods**: Multiple `LeftJoin()` overloads on `IQueryable<T>`
142+
- **Result type**: Returns `LeftJoinResult<TOuter, TInner>` with nullable inner entity
143+
- **Query translation**: Custom expression visitors translate to SQL LEFT JOIN
144+
- **Type safety**: Strongly-typed results with proper null handling
145+
146+
**Table Hints (SQL Server)**:
147+
- **Type-safe API**: `WithTableHints()` extension accepts `SqlServerTableHint` enum
148+
- **Query integration**: Hints applied via custom query translation
149+
- **Common hints**: `NoLock`, `ReadPast`, `UpdLock`, `HoldLock`, `RowLock`, `PageLock`, `TabLock`
150+
- **Limited variants**: `SqlServerTableHintLimited` for restricted contexts
151+
152+
**Nested Transactions**:
153+
- **Manager**: `NestedRelationalTransactionManager` wraps EF Core transaction manager
154+
- **Transaction types**: `RootNestedDbContextTransaction` (physical), `ChildNestedDbContextTransaction` (logical)
155+
- **Savepoint simulation**: Child transactions use commit/rollback tracking without actual nested transactions
156+
- **Lifecycle tracking**: Maintains transaction stack for proper cleanup
157+
158+
**Collection Parameters**:
159+
- **Scalar collections**: `ScalarCollectionParameter<T>` for primitive value lists
160+
- **Factory pattern**: `ICollectionParameterFactory` creates provider-specific implementations
161+
- **SQL Server JSON**: `JsonCollectionParameter` uses JSON for complex collections
162+
- **Query translation**: Integrates with EF Core query pipeline for parameterized queries
163+
164+
**Tenant Database Support**:
165+
- **Provider interface**: `ITenantDatabaseProvider` abstracts tenant-specific database selection
166+
- **Factory**: `ITenantDatabaseProviderFactory` creates providers per query context
167+
- **Query integration**: Custom query context factory injects tenant information
168+
- **Dummy implementation**: Default no-op provider for single-database scenarios
169+
170+
**Entity Data Readers**:
171+
- **ADO.NET bridge**: `IEntityDataReader` exposes entities as `IDataReader`
172+
- **Bulk operation support**: Enables `SqlBulkCopy` to read from entity collections
173+
- **Property navigation**: `PropertyWithNavigations` handles complex property paths
174+
- **Value extraction**: Supports regular properties, shadow properties, and navigation properties
175+
- **Factory pattern**: `IEntityDataReaderFactory` creates readers from entity collections
54176

55177
**Multi-Provider Support**:
56-
- Shared interfaces in BulkOperations with provider-specific implementations
57-
- SQL Server: Table hints, temp tables, window functions, tenant database support
58-
- SQLite: Simplified bulk operations, window functions, limitation handling
178+
- **Layered abstractions**: Relational → BulkOperations → Provider-specific
179+
- **Shared infrastructure**: Common code in base packages; providers implement specifics
180+
- **SQL Server features**: Table hints, JSON parameters, MERGE statements, temp tables
181+
- **SQLite adaptations**: Simplified bulk operations, command builders, dialect handling
182+
- **Extension points**: Providers customize query translation, SQL generation, and migration handling
59183

60184
## Testing Infrastructure
61185

@@ -118,25 +242,157 @@ This repository extends Entity Framework Core with performance enhancements and
118242
- SQLite: Account for concurrency limitations, DDL locks, missing server features
119243
- Add conditional code paths only when necessary; maintain consistent API surface where possible
120244

121-
### Essential Patterns
245+
### Essential Patterns and API Usage
246+
247+
#### Bulk Operations
248+
```csharp
249+
// Bulk insert entities
250+
await dbContext.BulkInsertAsync(entities, options =>
251+
{
252+
options.PropertyProvider = // optional: control which properties to insert
253+
});
254+
255+
// Bulk update entities
256+
await dbContext.BulkUpdateAsync(entities, options =>
257+
{
258+
options.PropertyProvider = // optional: control which properties to update
259+
});
260+
261+
// Bulk insert or update (upsert/MERGE)
262+
await dbContext.BulkInsertOrUpdateAsync(entities, options =>
263+
{
264+
// SQL Server: Uses MERGE statement
265+
// SQLite: Uses INSERT ... ON CONFLICT
266+
});
267+
268+
// Truncate table
269+
await dbContext.TruncateTableAsync<MyEntity>();
270+
```
271+
272+
#### Temp Tables
273+
```csharp
274+
// Create and populate temp table from entities
275+
await using var tempTable = await dbContext.BulkInsertIntoTempTableAsync(entities);
276+
277+
// Create and populate temp table from scalar values
278+
await using var tempTable = await dbContext.BulkInsertValuesIntoTempTableAsync(
279+
new[] { 1, 2, 3 },
280+
builder => builder.HasColumn<int>("Value")
281+
);
282+
283+
// Use temp table in queries
284+
var results = await tempTable.Query
285+
.Join(dbContext.Orders, t => t.Id, o => o.CustomerId, (t, o) => o)
286+
.ToListAsync();
287+
288+
// Temp table is automatically dropped when disposed
289+
```
290+
291+
#### Window Functions
292+
```csharp
293+
// Row number with partition and order
294+
var query = dbContext.Orders
295+
.Select(o => new
296+
{
297+
Order = o,
298+
RowNum = EF.Functions.RowNumber(
299+
EF.Functions.PartitionBy(o.CustomerId),
300+
EF.Functions.OrderBy(o.OrderDate)
301+
)
302+
});
303+
304+
// Multiple partitions and ordering
305+
var rowNum = EF.Functions.RowNumber(
306+
EF.Functions.PartitionBy(o.Category, o.Region),
307+
EF.Functions.OrderBy(o.Date).ThenByDescending(o.Amount)
308+
);
309+
```
310+
311+
#### LEFT JOIN
312+
```csharp
313+
// LEFT JOIN with null-safe result
314+
var query = dbContext.Customers
315+
.LeftJoin(
316+
dbContext.Orders,
317+
customer => customer.Id,
318+
order => order.CustomerId,
319+
(customer, order) => new { Customer = customer, Order = order.Value }
320+
)
321+
.Where(x => x.Order == null || x.Order.Amount > 100);
322+
```
323+
324+
#### Table Hints (SQL Server)
325+
```csharp
326+
// Single hint
327+
var query = dbContext.Orders
328+
.WithTableHints(SqlServerTableHint.NoLock);
329+
330+
// Multiple hints
331+
var query = dbContext.Orders
332+
.WithTableHints(SqlServerTableHint.NoLock, SqlServerTableHint.ReadPast);
333+
```
334+
335+
#### Nested Transactions
336+
```csharp
337+
// Outer transaction
338+
await using var transaction1 = await dbContext.Database.BeginTransactionAsync();
339+
340+
// Nested transaction (logical savepoint)
341+
await using var transaction2 = await dbContext.Database.BeginTransactionAsync();
342+
343+
await transaction2.CommitAsync(); // Commits nested transaction
344+
await transaction1.CommitAsync(); // Commits outer transaction
345+
```
346+
347+
#### Property Selection in Bulk Operations
122348
```csharp
123-
// Proper async method signature
349+
// Include specific properties
350+
await dbContext.BulkInsertAsync(entities, options =>
351+
{
352+
options.PropertyProvider = IncludingEntityPropertiesProvider.Include(
353+
e => e.Name,
354+
e => e.Email
355+
);
356+
});
357+
358+
// Exclude specific properties
359+
await dbContext.BulkUpdateAsync(entities, options =>
360+
{
361+
options.PropertyProvider = ExcludingEntityPropertiesProvider.Exclude(
362+
e => e.CreatedAt,
363+
e => e.Id
364+
);
365+
});
366+
```
367+
368+
#### Collection Parameters
369+
```csharp
370+
// Create scalar collection parameter
371+
var param = dbContext.CreateScalarCollectionParameter(new[] { 1, 2, 3 });
372+
373+
// Use in queries (provider translates to appropriate SQL)
374+
var orders = await dbContext.Orders
375+
.Where(o => param.Contains(o.CustomerId))
376+
.ToListAsync();
377+
```
378+
379+
#### General Async Patterns
380+
```csharp
381+
// Proper async method signature with cancellation
124382
public async Task<Result> ProcessAsync(CancellationToken cancellationToken = default)
125383
{
126384
ArgumentNullException.ThrowIfNull(parameter);
127385
var data = await GetDataAsync(cancellationToken);
128386
return ProcessData(data);
129387
}
130388

131-
// Bulk operations usage
132-
await ActDbContext.BulkInsertAsync(entities);
133-
await using var tempTable = await ActDbContext.BulkInsertIntoTempTableAsync(values);
134-
135-
// Table hints (SQL Server)
136-
query.WithTableHints(SqlServerTableHint.NoLock)
389+
// Always prefer server-side evaluation
390+
var query = dbContext.Orders
391+
.Where(o => o.Status == OrderStatus.Pending) // Server-side
392+
.AsNoTracking(); // Read-only optimization
137393
138-
// Window functions
139-
EF.Functions.WindowFunction(function, arg, EF.Functions.PartitionBy(...), EF.Functions.OrderBy(...))
394+
// Avoid client evaluation
395+
var results = await query.ToListAsync(); // Execute on server first
140396
```
141397

142398
## Important Files to Know

0 commit comments

Comments
 (0)