Skip to content
Open
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
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ using (var conn = new NpgsqlConnection("ConnectionString"))
}
```

## Custom Database Adapter

If you need to support a database that is not included, or want to provide your own implementation of `IDbAdapter`, you can do so:

```csharp
var options = new RespawnerOptions
{
DbAdapter = new MyCustomDbAdapter()
};
var respawner = await Respawner.CreateAsync(connection, options);
```

`myCustomDbAdapter` should implement the `IDbAdapter` interface.

This allows you to extend Respawn for any database or custom logic you require.

For a very simple example of a custom IDbAdapter being used see the [SqliteCustomAdapterTests](Respawn.DatabaseTests\SqliteCustomAdapterTests.cs)

## How does it work?
Respawn examines the SQL metadata intelligently to build a deterministic order of tables to delete based on foreign key relationships between tables. It navigates these relationships to build a DELETE script starting with the tables with no relationships and moving inwards until all tables are accounted for.

Expand Down
1 change: 1 addition & 0 deletions Respawn.DatabaseTests/Respawn.DatabaseTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IBM.Data.DB2.Core" Version="3.1.0.500" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="MySql.Data" Version="9.2.0" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="NPoco.SqlServer" Version="5.7.1" />
Expand Down
61 changes: 61 additions & 0 deletions Respawn.DatabaseTests/SqliteCustomAdapterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Data.Common;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using Respawn;
using Respawn.Graph;
using Shouldly;
using Xunit;

namespace Respawn.DatabaseTests
{
public class CustomSqliteAdapter : IDbAdapter
{
public string BuildDeleteCommandText(GraphBuilder graph, RespawnerOptions options)
=> "DELETE FROM TestTable;";
public string BuildReseedSql(System.Collections.Generic.IEnumerable<Table> tables) => null;
public string BuildTableCommandText(RespawnerOptions options) => "SELECT NULL, name FROM sqlite_master WHERE type='table' AND name='TestTable';";
public string BuildRelationshipCommandText(RespawnerOptions options) => "SELECT NULL, 'TestTable', NULL, 'TestTable', 'PK_TestTable';";
public string BuildTemporalTableCommandText(RespawnerOptions options) => "SELECT NULL, NULL, NULL, NULL;";
public string BuildTurnOffSystemVersioningCommandText(System.Collections.Generic.IEnumerable<TemporalTable> tables) => string.Empty;
public string BuildTurnOnSystemVersioningCommandText(System.Collections.Generic.IEnumerable<TemporalTable> tables) => string.Empty;
public Task<bool> CheckSupportsTemporalTables(DbConnection connection) => Task.FromResult(false);
}

public class SqliteCustomAdapterTests
{
[Fact]
public async Task ShouldResetTableWithCustomAdapter()
{
var connectionString = "Data Source=:memory:";
await using var conn = new SqliteConnection(connectionString);
await conn.OpenAsync();

var createTable = conn.CreateCommand();
createTable.CommandText = "CREATE TABLE TestTable (Id INTEGER PRIMARY KEY, Name TEXT);";
await createTable.ExecuteNonQueryAsync();

var insert = conn.CreateCommand();
insert.CommandText = "INSERT INTO TestTable (Name) VALUES ('foo');";
await insert.ExecuteNonQueryAsync();

// Confirm row exists
var countCmd = conn.CreateCommand();
countCmd.CommandText = "SELECT COUNT(*) FROM TestTable;";
var countBefore = (long)await countCmd.ExecuteScalarAsync();
countBefore.ShouldBe(1);

// Use custom adapter
var options = new RespawnerOptions
{
DbAdapter = new CustomSqliteAdapter(),
};
var respawner = await Respawner.CreateAsync(conn, options);
await respawner.ResetAsync(conn);

// Confirm table is empty
var countAfter = (long)await countCmd.ExecuteScalarAsync();
countAfter.ShouldBe(0);
}
}
}