Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sqlite): add sqlite support #38

Merged
merged 25 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2184ca8
chore: clean up
daniel-buchanan Mar 15, 2024
ef196cf
feat(sqlite): add project
daniel-buchanan Mar 15, 2024
d2b84e8
feat(sqlite): add options
daniel-buchanan Mar 15, 2024
0c8692b
feat(sqlite): add extensions
daniel-buchanan Mar 15, 2024
eb8a4a4
feat(sqlite): add builders / pipelines
daniel-buchanan Mar 15, 2024
1639be9
feat(sqlite): add connections
daniel-buchanan Mar 15, 2024
45bed16
feat(sqlite): add value parser
daniel-buchanan Mar 15, 2024
ef9d772
feat(sqlite): add implementation factory
daniel-buchanan Mar 15, 2024
3065d82
feat(sqlite): update pipelines
daniel-buchanan Mar 15, 2024
094c80c
Merge branch 'main' into f_sqlite
daniel-buchanan Apr 7, 2024
f7e9fb8
feat(sqlite): add tests
daniel-buchanan Apr 20, 2024
8f90a51
fix: add sqlite to coverage
daniel-buchanan Apr 20, 2024
59bac8f
fix: fix issues raised by sonarcloud
daniel-buchanan Apr 20, 2024
5b22649
tests: add coverage for options builder
daniel-buchanan Apr 20, 2024
6156ede
chore: remove unecessary code
daniel-buchanan Apr 20, 2024
bfff1f8
Merge branch 'main' into f_sqlite
daniel-buchanan May 12, 2024
7369516
feat(sqlite): add ddl and e2e tests
daniel-buchanan Jun 3, 2024
90f4a6e
chore: fixing code smells
daniel-buchanan Jun 8, 2024
2335185
chore: fixing code smells
daniel-buchanan Jun 8, 2024
c323977
chore: fix code smells
daniel-buchanan Jun 8, 2024
e6a65c3
chore: fix code smells
daniel-buchanan Jun 8, 2024
9c09576
tests: tidy up tests for ddl
daniel-buchanan Jun 8, 2024
adfb723
chore: update sonar checks to net8
daniel-buchanan Jun 9, 2024
4fcaeac
tests: fix type parser
daniel-buchanan Jun 9, 2024
31ed660
chore: update readme
daniel-buchanan Jun 9, 2024
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
10 changes: 10 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
- 'src/db/decaf.npgsql/**'
sqlserver:
- 'src/db/decaf.sqlserver/**'
sqlite:
- 'src/db/decaf.sqlite/**'
services:
- 'src/extensions/decaf.services/**'
logging_serilog:
Expand All @@ -44,6 +46,7 @@ jobs:
run-db: ${{ steps.changes.outputs.db }}
run-npgsql: ${{ steps.changes.outputs.npgsql }}
run-sqlserver: ${{ steps.changes.outputs.sqlserver }}
run-sqlite: ${{ steps.changes.outputs.sqlite }}
run-services: ${{ steps.changes.outputs.services }}
run-logging-serilog: ${{ steps.changes.outputs.logging_serilog }}

Expand Down Expand Up @@ -74,6 +77,13 @@ jobs:
if: ${{ needs.check-paths.outputs.run-sqlserver == 'true' }}
uses: ./.github/workflows/publish-decaf.db.sqlserver.yml
secrets: inherit

pub-decafsqlite:
name: decaf.sqlite
needs: [check-paths]
if: ${{ needs.check-paths.outputs.run-sqlite == 'true' }}
uses: ./.github/workflows/publish-decaf.db.sqlite.yml
secrets: inherit

pub-decafservices:
name: decaf.services
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/publish-decaf.db.sqlite.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Publish
on:
workflow_call:
env:
FULL_PACKAGE_VERSION: ${{ format('0.1.{0}+{1}', github.run_number, github.sha) }}
PACKAGE_VERSION: ${{ format('0.1.{0}', github.run_number) }}
jobs:
pack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Pack
env:
CSPROJ: src/db/decaf.sqlite/decaf.sqlite-nuget.csproj
run: |
dotnet add ${CSPROJ} package decaf-orm --version ${{ vars.LATEST_DECAF }} --no-restore
dotnet add ${CSPROJ} package decaf-orm.db --version ${{ vars.LATEST_DB }} --no-restore
dotnet nuget locals all --clear
dotnet restore ${CSPROJ}
dotnet build ${CSPROJ} -c Release
dotnet pack ${CSPROJ} -p:Version=${FULL_PACKAGE_VERSION} -o ./output
- name: Save Archive
uses: actions/upload-artifact@v2.2.4
with:
name: package
path: ./output
retention-days: 0

publish:
runs-on: ubuntu-latest
needs: [pack]
steps:
- name: Download Artifact
uses: actions/download-artifact@v2.0.10
with:
name: package
path: ./output
- name: Upload to NuGet
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
PACKAGE_NAME: ${{ format('decaf-orm.sqlite.{0}.nupkg', env.PACKAGE_VERSION) }}
run: |
dotnet nuget push ./output/${PACKAGE_NAME} \
-k ${NUGET_API_KEY} \
-s https://api.nuget.org/v3/index.json
- name: Persist Version
env:
GH_TOKEN: ${{ secrets.GH_CLI_PAT }}
run: |
gh variable set LATEST_SQLITE --body=${PACKAGE_VERSION} -R daniel-buchanan/decaf-orm
14 changes: 14 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
- 'src/db/decaf.npgsql/**'
sqlserver:
- 'src/db/decaf.sqlserver/**'
sqlite:
- 'src/db/decaf.sqlite/**'
services:
- 'src/extensions/decaf.services/**'
logging:
Expand All @@ -29,6 +31,7 @@ jobs:
run-services: ${{ steps.changes.outputs.services }}
run-npgsql: ${{ steps.changes.outputs.npgsql }}
run-sqlserver: ${{ steps.changes.outputs.sqlserver }}
run-sqlite: ${{ steps.changes.outputs.sqlite }}
run-logging: ${{ steps.changes.outputs.logging }}

test-decaf:
Expand Down Expand Up @@ -80,6 +83,17 @@ jobs:
with:
job-name: decaf.sqlserver
test-project: tests/decaf.sqlserver.tests/decaf.sqlserver.tests.csproj

test-decafsqlite:
needs: [check-paths]
name: test-decafsqlite
if: ${{ needs.check-paths.outputs.run-sqlite == 'true' }}
uses: ./.github/workflows/test-run.yml
secrets: inherit
with:
job-name: decaf.sqlite
test-project: tests/decaf.sqlite.tests/decaf.sqlite.tests.csproj

test-decaflogging:
needs: [check-paths]
name: test-decaflogging
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<values>
<value name="VS.TelemetryApi.TotalDisposeLatency" type="int">12</value>
<value name="VS.TelemetryApi.ChannelsDisposeLatency" type="int">2</value>
<value name="VS.TelemetryApi.DroppedEventsDuringDisposing" type="int">0</value>
</values>
86 changes: 73 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,50 +110,110 @@ services.AddDecaf(o => {
});
```

Or, with SQLite:
```csharp
services.AddDecaf(o => {
o.UseSqlite(b =>
b.WithFilePath("/Users/dkb/Documents/mydb.db")
.AsReadOnly()
);
});
```

## Getting a Query
[Return to Top](#decaf)

decaf's default configuration provides an `IUnitOfWorkFactory` factory which is added to the service provider by default.
Once you have an `IUnitOfWork` created from the `IUnitOfWorkFactory`, you can begin to query it. Each query is managed by itself and executed seperately.
decaf's default configuration provides an `IDecaf` factory which is added to the service provider by default.
Once you have an `IUnitOfWork` created from the `IDecaf`, you can begin to query it. Each query is managed by itself and executed seperately.
You may also, at configuration time call `InjectUnitOfWorkAsScoped` or `InjectUnitOfWork` to inject an `IUnitOfWork` instance
into the pipeline either as scoped, or with whatever lifetime you require.
into the pipeline either as scoped, or with whatever lifetime you require and using the default bound `IConnectionDetails`.

> It is worth noting that it is the `IUnitOfWork` instance that controls the commit/rollback of the transaction which is associated with any queries created from it.
Once this is injected into your service, handler or other class it can be used in the following ways.

**Disposable:**
```csharp
public class Example(IUnitOfWorkFactory factory)
public class Example(IDecaf decaf)
{
public async Task DoStuff()
{
using var uow = await factory.CreateAsync();
uow.WithCatch((ex) => Console.Error.WriteLine(ex.Message))
.Query(q => ...)
.PersistChanges();
using var unit = await decaf.BuildUnitAsync();
unit.OnException((ex) => Console.Error.WriteLine(ex.Message))
.Query(q => ...);
}
}
```

> **Note:** In the disposable case *all* queries are executed on exit of the disposable scope.
> The queries are executed in the order that they are provided. As such you do not need to call `PersistChanges()` or `PersistChangesAsync()`
**Non-Disposable:**
```csharp
public class Example(IUnitOfWorkFactory factory)
public class Example(IDecaf decaf)
{
public async Task DoStuff()
{
var uow = await factory.CreateAsync();
uow.WithCatch((ex) => Console.Error.WriteLine(ex.Message))
.Query(q => ...)
.PersistChanges();
var unit = await decaf.BuildUnitAsync();
unit.OnException((ex) => Console.Error.WriteLine(ex.Message))
.Query(q => ...)
.PersistChanges();
}
}
```

> **Note:** using the "non-disposable" approach means that you need to call `PersistChanges` or `PersistChangesAsync` in order for any queries to be executed.
## Error Handling
There are several ways you can handle errors for the queries executed by decaf.
1. Using the provided `OnException` and `OnSuccess` methods - note there are also `async` versions of these methods.
2. Manually wrapping the execution in a `try ... catch`

> **Note:** there is a relevant option in the decaf options (provided when adding to the `IServiceCollection`) which allows for commit exceptions to be swallowed.
> If this is enabled then regardless of the behaviour of the `OnException` method, no exception will be thrown.

### OnException
The `OnException` and `OnExceptionAsync` methods provide a way to handle exceptions thrown by the query with your own error handling delegate.
There are in fact two versions of these methods:
1. `OnException(Action<Exception> handler)`
This takes a delegate (or lambda) which does... something with exception, it could just log it for example.
It is worth noting with the usage of this method, is that any exceptions handled by your own delegate *will **not*** be re-thrown, so it is up to you to do anything you need to do.
2. `OnException(Func<Exception, bool> handler)`
This takes a function delegate (or lambda) which handles the exception **and** returns a `true` or `false` value.
In this case, returning false is the same as using the above delegate which has no return type.
If however, you return `true` from your handler, the exception will be re-thrown.

**Example:**
```csharp
private void DoStuff()
{
decaf.OnException(MyExceptionHandler);
}

private void MyExceptionHandler(Exception e)
=> _logger.Error(e);
```

### OnSuccess
The `OnSuccess`and `OnSuccessAsync` methods allow you to provide a delegate if you want to perform logic at the finish of the query.
This does not include any information from the query at this stage.

**Example:**
```csharp
private void DoStuff()
{
decaf.OnException(MyExceptionHandler)
.OnSuccess(MySuccessHandler);
}

private void MyExceptionHandler(Exception e)
=> _logger.Error(e);

private void MySuccessHandler()
=> _logger.Information("query succeeded");
```

## Creating a Simple Query
[Return to Top](#decaf)

Expand Down
21 changes: 14 additions & 7 deletions build/sonar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,51 @@ echo "SONAR_TOKEN=${SONAR_TOKEN}";

dotnet build --no-incremental decaf-orm.sln

./.coverlet/coverlet ./tests/decaf.db.common.tests/bin/Debug/net7.0/decaf.db.common.tests.dll \
./.coverlet/coverlet ./tests/decaf.db.common.tests/bin/Debug/net8.0/decaf.db.common.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
-f=json \
-o="db-common.json"

./.coverlet/coverlet ./tests/decaf.services.tests/bin/Debug/net7.0/decaf.services.tests.dll \
./.coverlet/coverlet ./tests/decaf.services.tests/bin/Debug/net8.0/decaf.services.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
--merge-with "db-common.json" \
-f=json \
-o="services-merged.json"

./.coverlet/coverlet ./tests/decaf.logging.tests/bin/Debug/net7.0/decaf.logging.tests.dll \
./.coverlet/coverlet ./tests/decaf.logging.tests/bin/Debug/net8.0/decaf.logging.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
--merge-with "services-merged.json" \
-f=json \
-o="logging-merged.json"

./.coverlet/coverlet ./tests/decaf.npgsql.tests/bin/Debug/net7.0/decaf.npgsql.tests.dll \
./.coverlet/coverlet ./tests/decaf.npgsql.tests/bin/Debug/net8.0/decaf.npgsql.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
--merge-with "logging-merged.json" \
-f=json \
-o="npgsql-merged.json"

./.coverlet/coverlet ./tests/decaf.sqlserver.tests/bin/Debug/net7.0/decaf.sqlserver.tests.dll \
./.coverlet/coverlet ./tests/decaf.sqlserver.tests/bin/Debug/net8.0/decaf.sqlserver.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
--merge-with "npgsql-merged.json" \
-f=json \
-o="sqlserver-merged.json"

./.coverlet/coverlet ./tests/decaf.core.tests/bin/Debug/net7.0/decaf.core.tests.dll \
./.coverlet/coverlet ./tests/decaf.sqlite.tests/bin/Debug/net8.0/decaf.sqlite.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
--merge-with "sqlserver-merged.json" \
-f=json \
-o="sqlite-merged.json"

./.coverlet/coverlet ./tests/decaf.core.tests/bin/Debug/net8.0/decaf.core.tests.dll \
--target "dotnet" \
--targetargs "test --no-build" \
--merge-with "sqlite-merged.json" \
-f=opencover \
-o="coverage.xml"

Expand Down
14 changes: 14 additions & 0 deletions decaf-orm.sln
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "decaf.common", "src\core\de
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "decaf.state", "src\core\decaf.state\decaf.state.csproj", "{C6B3F81E-2872-4EA3-914C-97673665A788}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "decaf.sqlite", "src\db\decaf.sqlite\decaf.sqlite.csproj", "{51F3826D-FF3C-4357-81C4-5C6D2FE23AF5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "decaf.sqlite.tests", "tests\decaf.sqlite.tests\decaf.sqlite.tests.csproj", "{B45FFB72-C31C-4F0A-B705-2C2589647876}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -118,6 +122,14 @@ Global
{C6B3F81E-2872-4EA3-914C-97673665A788}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6B3F81E-2872-4EA3-914C-97673665A788}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6B3F81E-2872-4EA3-914C-97673665A788}.Release|Any CPU.Build.0 = Release|Any CPU
{51F3826D-FF3C-4357-81C4-5C6D2FE23AF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51F3826D-FF3C-4357-81C4-5C6D2FE23AF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51F3826D-FF3C-4357-81C4-5C6D2FE23AF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51F3826D-FF3C-4357-81C4-5C6D2FE23AF5}.Release|Any CPU.Build.0 = Release|Any CPU
{B45FFB72-C31C-4F0A-B705-2C2589647876}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B45FFB72-C31C-4F0A-B705-2C2589647876}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B45FFB72-C31C-4F0A-B705-2C2589647876}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B45FFB72-C31C-4F0A-B705-2C2589647876}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{AF343303-15C8-4717-9767-6991C1350A83} = {EA0A3240-4A8A-43DB-A307-B5E4A380C779}
Expand All @@ -139,5 +151,7 @@ Global
{941304FA-BFB6-4FF4-896A-011EB665AEF7} = {AF343303-15C8-4717-9767-6991C1350A83}
{47963E1C-7616-4DB0-B9FC-3C9417A9DFA0} = {AF343303-15C8-4717-9767-6991C1350A83}
{C6B3F81E-2872-4EA3-914C-97673665A788} = {AF343303-15C8-4717-9767-6991C1350A83}
{51F3826D-FF3C-4357-81C4-5C6D2FE23AF5} = {72A6BDCD-0207-446D-BC0E-C33198FB1248}
{B45FFB72-C31C-4F0A-B705-2C2589647876} = {3B21659C-528B-4CA8-B094-9C29659F4569}
EndGlobalSection
EndGlobal
3 changes: 1 addition & 2 deletions src/core/decaf.common/Connections/TransactionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ public async Task<ITransaction> GetAsync(IConnectionDetails connectionDetails, C

return transaction;
}

/// <inheritdoc/>

protected abstract Task<ITransaction> CreateTransactionAsync(IConnection connection, CancellationToken cancellationToken = default);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/decaf.common/IQueryContainerInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("decaf")]
namespace decaf.common
{
internal interface IQueryContainerInternal : IQueryContainer
public interface IQueryContainerInternal : IQueryContainer
{
/// <summary>
/// Get the hash that represents this query.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace decaf.common
{
internal interface IQueryContextInternal : IQueryContext
public interface IQueryContextExtended : IQueryContext
{
/// <summary>
/// The <see cref="IExpressionHelper"/> which provides helper methods for
Expand Down Expand Up @@ -65,7 +65,7 @@ internal interface IQueryContextInternal : IQueryContext
IQueryTarget GetQueryTarget(string alias);
}

internal interface IQueryParsers
public interface IQueryParsers
{
IParser Join { get; }

Expand Down
3 changes: 2 additions & 1 deletion src/core/decaf.common/ISqlFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ public interface ISqlFactory
/// </summary>
/// <param name="context"></param>
/// <param name="template"></param>
/// <param name="includePrefix">Whether or not to include the prefix for the parameter.</param>
/// <returns></returns>
object ParseParameters(IQueryContext context, SqlTemplate template);
object ParseParameters(IQueryContext context, SqlTemplate template, bool includePrefix = true);
}
}

6 changes: 2 additions & 4 deletions src/core/decaf.common/ManagedAlias.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("decaf.core.tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("decaf.state")]
namespace decaf.common
namespace decaf.common
{
internal class ManagedAlias
public class ManagedAlias
{
private ManagedAlias(string name, string relation)
{
Expand Down
Loading
Loading