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
21 changes: 18 additions & 3 deletions .github/workflows/ci-build-efcore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ jobs:
env:
ACCEPT_EULA: Y
SA_PASSWORD: ${{ env.db_pwd }}
mysql:
image: mysql:8.0
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: ${{ env.db_pwd }}
MYSQL_DATABASE: weasel_testing
MYSQL_USER: weasel
MYSQL_PASSWORD: ${{ env.db_pwd }}
options: >-
--health-cmd="mysqladmin ping -h localhost"
--health-interval=10s
--health-timeout=5s
--health-retries=5

steps:
- uses: actions/checkout@v6
Expand All @@ -60,7 +74,8 @@ jobs:
- name: Build
run: dotnet build src/Weasel.EntityFrameworkCore.Tests/Weasel.EntityFrameworkCore.Tests.csproj --no-restore --framework ${{ matrix.framework }}

# MySql and Oracle EF providers have no database services in this workflow;
# their end-to-end suites only run locally against docker-compose
# The Oracle EF provider has no database service in this workflow; its
# end-to-end suite only runs locally against docker-compose. MySql tests
# exist on net9.0 only (the provider does not yet support net10.0).
- name: Test
run: dotnet test src/Weasel.EntityFrameworkCore.Tests/Weasel.EntityFrameworkCore.Tests.csproj --no-build --verbosity normal --framework ${{ matrix.framework }} --filter "FullyQualifiedName!~MySql&FullyQualifiedName!~Oracle"
run: dotnet test src/Weasel.EntityFrameworkCore.Tests/Weasel.EntityFrameworkCore.Tests.csproj --no-build --verbosity normal --framework ${{ matrix.framework }} --filter "FullyQualifiedName!~Oracle"
4 changes: 2 additions & 2 deletions docs/core/command-builders.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Each database provider has its own `ICommandBuilder` interface and `BatchBuilder
The provider-specific `ICommandBuilder` interface (defined in both `Weasel.Postgresql` and `Weasel.SqlServer`) exposes methods for building SQL incrementally:

<!-- snippet: sample_ICommandBuilder_interface -->
<a id='snippet-sample_ICommandBuilder_interface'></a>
<a id='snippet-sample_icommandbuilder_interface'></a>
```cs
public interface ICommandBuilder_Sample
{
Expand All @@ -31,7 +31,7 @@ public interface ICommandBuilder_Sample
// ... additional members
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/CommandBuilderSamples.cs#L6-L19' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ICommandBuilder_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/CommandBuilderSamples.cs#L6-L19' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_icommandbuilder_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Key methods:
Expand Down
16 changes: 8 additions & 8 deletions docs/core/multi-tenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ In a sharded multi-tenant architecture, each tenant's data lives in one of sever
Manages the registry of databases and tenant assignments:

<!-- snippet: sample_ITenantDatabasePool_interface -->
<a id='snippet-sample_ITenantDatabasePool_interface'></a>
<a id='snippet-sample_itenantdatabasepool_interface'></a>
```cs
public interface ITenantDatabasePool_Sample
{
Expand All @@ -30,7 +30,7 @@ public interface ITenantDatabasePool_Sample
ValueTask RemoveTenantAsync(string tenantId, CancellationToken ct);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L7-L17' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ITenantDatabasePool_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L7-L17' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_itenantdatabasepool_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Each `PooledDatabase` tracks a database's identifier, connection string, and whether it is full (accepting no more tenants).
Expand All @@ -40,7 +40,7 @@ Each `PooledDatabase` tracks a database's identifier, connection string, and whe
Determines which database a new tenant should be assigned to:

<!-- snippet: sample_ITenantAssignmentStrategy_interface -->
<a id='snippet-sample_ITenantAssignmentStrategy_interface'></a>
<a id='snippet-sample_itenantassignmentstrategy_interface'></a>
```cs
public interface ITenantAssignmentStrategy_Sample
{
Expand All @@ -49,7 +49,7 @@ public interface ITenantAssignmentStrategy_Sample
IReadOnlyList<PooledDatabase> availableDatabases);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L19-L26' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ITenantAssignmentStrategy_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L19-L26' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_itenantassignmentstrategy_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Weasel ships with several built-in strategies:
Expand All @@ -65,30 +65,30 @@ Weasel ships with several built-in strategies:
Controls when a database is considered "full" and should stop accepting new tenants:

<!-- snippet: sample_IDatabaseSizingStrategy_interface -->
<a id='snippet-sample_IDatabaseSizingStrategy_interface'></a>
<a id='snippet-sample_idatabasesizingstrategy_interface'></a>
```cs
public interface IDatabaseSizingStrategy_Sample
{
ValueTask<string> FindSmallestDatabaseAsync(IReadOnlyList<PooledDatabase> databases);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L28-L33' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_IDatabaseSizingStrategy_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L28-L33' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_idatabasesizingstrategy_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## IDatabase.TenantIds

The `IDatabase` interface includes a `TenantIds` property that lists which tenants are assigned to that database instance:

<!-- snippet: sample_IDatabase_TenantIds -->
<a id='snippet-sample_IDatabase_TenantIds'></a>
<a id='snippet-sample_idatabase_tenantids'></a>
```cs
public interface IDatabase_TenantIds_Sample
{
List<string> TenantIds { get; }
// ... other members
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L37-L43' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_IDatabase_TenantIds' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/MultiTenancySamples.cs#L37-L43' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_idatabase_tenantids' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

This is used by the migration infrastructure to apply schema changes to the correct databases when running in a multi-tenant configuration.
Expand Down
12 changes: 6 additions & 6 deletions docs/core/schema-migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ flowchart TD
`IDatabase` (in `Weasel.Core.Migrations`) is the central interface for managing a database's schema lifecycle:

<!-- snippet: sample_IDatabase_interface -->
<a id='snippet-sample_IDatabase_interface'></a>
<a id='snippet-sample_idatabase_interface'></a>
```cs
public interface IDatabase_Sample
{
Expand All @@ -48,7 +48,7 @@ public interface IDatabase_Sample
string ToDatabaseScript();
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaMigrationSamples.cs#L8-L31' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_IDatabase_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaMigrationSamples.cs#L8-L31' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_idatabase_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Key methods:
Expand All @@ -66,7 +66,7 @@ Key methods:
An `IFeatureSchema` groups related schema objects together (for example, all the tables and indexes for a document storage feature):

<!-- snippet: sample_IFeatureSchema_interface -->
<a id='snippet-sample_IFeatureSchema_interface'></a>
<a id='snippet-sample_ifeatureschema_interface'></a>
```cs
public interface IFeatureSchema_Sample
{
Expand All @@ -76,7 +76,7 @@ public interface IFeatureSchema_Sample
Type StorageType { get; }
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaMigrationSamples.cs#L33-L41' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_IFeatureSchema_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaMigrationSamples.cs#L33-L41' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ifeatureschema_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Weasel processes features in the order returned by `BuildFeatureSchemas()`, so dependency relationships between features should be reflected by their position in the array.
Expand Down Expand Up @@ -186,15 +186,15 @@ var script = database.ToDatabaseScript();
Implement `IMigrationLogger` to capture the SQL that Weasel generates:

<!-- snippet: sample_IMigrationLogger_interface -->
<a id='snippet-sample_IMigrationLogger_interface'></a>
<a id='snippet-sample_imigrationlogger_interface'></a>
```cs
public interface IMigrationLogger_Sample
{
void SchemaChange(string sql);
void OnFailure(DbCommand command, Exception ex);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaMigrationSamples.cs#L43-L49' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_IMigrationLogger_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaMigrationSamples.cs#L43-L49' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_imigrationlogger_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The default logger writes SQL to `Console.WriteLine` and rethrows exceptions.
Expand Down
16 changes: 8 additions & 8 deletions docs/core/schema-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The diagram above shows the core type system. `ISchemaObject` implementations (t
Defined in `Weasel.Core`, the interface looks like this:

<!-- snippet: sample_ISchemaObject_interface -->
<a id='snippet-sample_ISchemaObject_interface'></a>
<a id='snippet-sample_ischemaobject_interface'></a>
```cs
public interface ISchemaObject_Sample
{
Expand All @@ -126,7 +126,7 @@ public interface ISchemaObject_Sample
IEnumerable<DbObjectName> AllNames();
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L6-L17' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ISchemaObject_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L6-L17' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ischemaobject_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

| Member | Purpose |
Expand All @@ -143,7 +143,7 @@ public interface ISchemaObject_Sample
When Weasel compares what you *configured* against what *actually exists* in the database, the result is an `ISchemaObjectDelta`:

<!-- snippet: sample_ISchemaObjectDelta_interface -->
<a id='snippet-sample_ISchemaObjectDelta_interface'></a>
<a id='snippet-sample_ischemaobjectdelta_interface'></a>
```cs
public interface ISchemaObjectDelta_Sample
{
Expand All @@ -154,7 +154,7 @@ public interface ISchemaObjectDelta_Sample
void WriteRestorationOfPreviousState(Migrator rules, TextWriter writer);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L19-L28' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ISchemaObjectDelta_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L19-L28' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ischemaobjectdelta_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The `Difference` property tells you the outcome of the comparison using `SchemaPatchDifference`:
Expand Down Expand Up @@ -183,7 +183,7 @@ Each database provider supplies its own concrete implementations of `ISchemaObje
Weasel provides a generic base class `SchemaObjectDelta<T>` that simplifies building deltas for a specific schema object type:

<!-- snippet: sample_SchemaObjectDelta_base_class -->
<a id='snippet-sample_SchemaObjectDelta_base_class'></a>
<a id='snippet-sample_schemaobjectdelta_base_class'></a>
```cs
public abstract class SchemaObjectDelta_Sample<T> : ISchemaObjectDelta where T : ISchemaObject
{
Expand All @@ -199,7 +199,7 @@ public abstract class SchemaObjectDelta_Sample<T> : ISchemaObjectDelta where T :
public abstract void WriteRestorationOfPreviousState(Migrator rules, TextWriter writer);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L30-L44' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_SchemaObjectDelta_base_class' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L30-L44' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_schemaobjectdelta_base_class' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The constructor calls `compare()` to determine the `Difference` between the expected and actual objects. If `Actual` is null, the object does not exist yet and the difference is `Create`.
Expand All @@ -209,14 +209,14 @@ The constructor calls `compare()` to determine the `Difference` between the expe
Some schema objects need to examine other objects before they can finalize their own configuration. For example, PostgreSQL partitioned tables may need to adjust foreign key definitions based on the partition strategy of related tables.

<!-- snippet: sample_ISchemaObjectWithPostProcessing_interface -->
<a id='snippet-sample_ISchemaObjectWithPostProcessing_interface'></a>
<a id='snippet-sample_ischemaobjectwithpostprocessing_interface'></a>
```cs
public interface ISchemaObjectWithPostProcessing_Sample : ISchemaObject
{
void PostProcess(ISchemaObject[] allObjects);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L46-L51' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ISchemaObjectWithPostProcessing_interface' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/SchemaObjectSamples.cs#L46-L51' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ischemaobjectwithpostprocessing_interface' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The migration infrastructure calls `PostProcess()` after all objects have been loaded, passing the full array of schema objects so the implementing object can make any cross-object adjustments.
Expand Down
4 changes: 2 additions & 2 deletions docs/efcore/json-columns.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class OrderDbContext : DbContext
}
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L128-L152' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_json_column_configuration' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L140-L164' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_json_column_configuration' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The resulting Weasel table will include three columns:
Expand Down Expand Up @@ -80,5 +80,5 @@ if (migration.Migration.Difference != SchemaPatchDifference.None)
await migration.ExecuteAsync(AutoCreate.CreateOrUpdate, ct);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L114-L124' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_json_migration_example' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L126-L136' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_json_migration_example' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
6 changes: 3 additions & 3 deletions docs/efcore/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if (migration.Migration.Difference != SchemaPatchDifference.None)
await migration.ExecuteAsync(AutoCreate.CreateOrUpdate, ct);
}
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L72-L82' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_create_migration' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L84-L94' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_create_migration' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The `DbContextMigration` record wraps three components:
Expand All @@ -44,7 +44,7 @@ var database = serviceProvider.CreateDatabase(dbContext);
// You can also provide a custom identifier:
var customDatabase = serviceProvider.CreateDatabase(dbContext, "my-read-models");
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L87-L94' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_create_database' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L99-L106' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_create_database' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

This is useful when composing multiple schema sources (e.g., Marten documents plus EF Core tables) into a single migration pipeline.
Expand All @@ -64,7 +64,7 @@ services.AddSingleton<Migrator>(new PostgresqlMigrator());
// Later, resolve automatically
var (connection, migrator) = serviceProvider.FindMigratorForDbContext(dbContext);
```
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L99-L107' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_find_migrator' title='Start of snippet'>anchor</a></sup>
<sup><a href='https://github.com/JasperFx/weasel/blob/master/src/DocSamples/EfCoreSamples.cs#L111-L119' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_efcore_find_migrator' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

If no registered `Migrator` matches the connection type, an `InvalidOperationException` is thrown listing the available migrators.
Expand Down
Loading
Loading