Skip to content

Commit

Permalink
Merge pull request #38 from daniel-buchanan/f_sqlite
Browse files Browse the repository at this point in the history
feat(sqlite): add sqlite support
  • Loading branch information
daniel-buchanan committed Jun 9, 2024
2 parents e662d83 + 31ed660 commit b823303
Show file tree
Hide file tree
Showing 176 changed files with 4,618 additions and 994 deletions.
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

0 comments on commit b823303

Please sign in to comment.