diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..2ef9b3c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,29 @@ +# DirectSQL Copilot Instructions + +## Build and test + +- Build the solution from the repo root with `dotnet build --configuration Release`. +- Run the full test suite with `dotnet test --configuration Release`. +- Run a single MSTest with `dotnet test TestSqlLiteDatabase/TestSqlLiteDatabase.csproj --filter FullyQualifiedName~TestSqlLiteDatabase.QueryTest.TestQuery`. +- The projects target `net8.0`. CI uses .NET 8.0.100, and `global.json` allows rolling forward from 7.0.100 to a newer major SDK. +- `DirectSQL/DirectSQL.csproj` has `GeneratePackageOnBuild=true`, so builds also produce a NuGet package in `DirectSQL/bin//`. +- In the current dependency set, SQLite tests fail on macOS before assertions run because `System.Data.SQLite.Core` cannot load `SQLite.Interop.dll`. + +## High-level architecture + +- `DirectSQL/Database.cs` is the core of the library. It owns connection lifecycle (`Process`, `ProcessAsync`), transaction wrappers, parameter creation/conversion, and the provider-agnostic SQL helpers (`ExecuteNonQuery`, `ExecuteScalar`, `Query`, `LoadSqlResult`, and the `FormattableString` overloads). +- Each database-specific entry point is a thin subclass of `Database<...>` under `DirectSQL/SqlLite`, `DirectSQL/SqlServer`, `DirectSQL/npgsql`, `DirectSQL/MySql`, `DirectSQL/DB2`, and `DirectSQL/Odbc`. These classes mostly capture a connection string and implement `CreateConnection()`. +- `DirectSQL/SqlResult.cs` wraps the active `IDataReader` and exposes the current row as `dynamic`, tuple arrays, typed projections, and enumerable loaders. This is the main abstraction for reading query results. +- The normal execution flow is: instantiate a concrete provider database -> enter `Process(...)` / `ProcessAsync(...)` to get an open connection -> call static query helpers on the provider type -> let `Database.cs` build the command and parameters -> read rows through `SqlResult`. +- `LoadSqlResult*` convenience APIs are thin wrappers over `Query(...)` that materialize the cursor into `dynamic[]`; they do not introduce a separate query pipeline. +- Error handling is centralized in `Database.Transaction(...)` and `Database.TransactionAsync(...)`: exceptions from the callback are wrapped in `DatabaseException`, while the low-level command and reader behavior remains straight ADO.NET. +- `TestSqlLiteDatabase` is the only test project and doubles as the main executable specification of library behavior. Most usage patterns in the README are mirrored there. + +## Key conventions + +- Treat the concrete `*Database` class as the entry point for connection management, then use static methods on that same concrete type inside the callback. The common pattern is `db.Process((conn, tran) => { SqlLiteDatabase.Query(...); })`, not instance methods for query execution. +- Transaction callbacks are manual-commit. `Process((conn, tran) => ...)` and `Database.Transaction(...)` provide a transaction object, but the library does not commit for you; tests that need persistence call `tran.Commit()` explicitly and use `tran.Rollback()` explicitly for rollback scenarios. The library only rolls back automatically when it catches an exception and rethrows `DatabaseException`. +- `SqlResult` is cursor-based. Call `result.Next()` before reading `ResultValues` or `ResultTuples`; attempting to read row values before the first `Next()` or after the cursor is exhausted raises the underlying reader error. `ResultValues` and `ResultTuples` are cached for the current row and reset on the next `Next()`. +- Prefer the built-in parameter helpers over handwritten command setup. The codebase uses tuple parameters like `new (string, object)[] { ("@val1", value) }` and `CreateParameter(...)` when an explicit `DbType` is needed. +- Prefer the `FormattableString` helpers when authoring inline SQL in code. `ExecuteFormattableNonQuery`, `ExecuteFormattableScalar`, `QueryFormattable`, and `LoadFormattableSqlResult` rewrite interpolated values into generated bound parameters like `@0`, `@1`, and tests assert against that rewritten SQL. +- The async APIs are wrappers around synchronous ADO.NET work via `Task.Run`, not provider-native async I/O. Keep that in mind when changing behavior or adding new async surfaces. diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4fc9244..0c36c50 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -59,4 +59,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 258886f..88beb2b 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -8,11 +8,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v6 - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 5.0.100-rc.2.20479.15 + dotnet-version: 8.0.100 source-url: https://nuget.pkg.github.com/directsql/index.json env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/DirectSQL/Database.cs b/DirectSQL/Database.cs index fd06f7a..ce2747f 100644 --- a/DirectSQL/Database.cs +++ b/DirectSQL/Database.cs @@ -278,7 +278,7 @@ T transaction { return ExecuteNonQuery( sql, - new ValueTuple[0], + Array.Empty<(string, object)>(), connection, transaction ); @@ -298,7 +298,7 @@ C connection { return ExecuteNonQuery( sql, - new ValueTuple[0], + Array.Empty<(string, object)>(), connection ); } @@ -393,7 +393,7 @@ public static object ExecuteScalar( { return ExecuteScalar( sql, - new ValueTuple[0], + Array.Empty<(string, object)>(), connection, transaction ); @@ -412,7 +412,7 @@ public static object ExecuteScalar( { return ExecuteScalarCore( sql, - new P[0], + Array.Empty

(), connection, DefaultTransaction.defaultTransaction ); diff --git a/DirectSQL/DatabaseDictionary.cs b/DirectSQL/DatabaseDictionary.cs new file mode 100644 index 0000000..95ccd52 --- /dev/null +++ b/DirectSQL/DatabaseDictionary.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DirectSQL +{ + internal class DatabaseDictionary + { + } +} diff --git a/DirectSQL/DirectSQL.csproj b/DirectSQL/DirectSQL.csproj index 99cd00e..08faa6e 100644 --- a/DirectSQL/DirectSQL.csproj +++ b/DirectSQL/DirectSQL.csproj @@ -1,9 +1,9 @@  - net5.0 + net8.0 true - 3.8.0 + 3.16.0 LICENSE https://github.com/DirectSQL/DirectSQL/ @@ -15,9 +15,9 @@ This .NET library is very thin database framework on top of System.Data. - + - + diff --git a/DirectSQL/SqlServer/SqlServerDatabase.cs b/DirectSQL/SqlServer/SqlServerDatabase.cs index 9a346a6..40e85b8 100644 --- a/DirectSQL/SqlServer/SqlServerDatabase.cs +++ b/DirectSQL/SqlServer/SqlServerDatabase.cs @@ -1,6 +1,6 @@ using System; -using System.Data.SqlClient; +using Microsoft.Data.SqlClient; namespace DirectSQL.SqlServer { diff --git a/TestSqlLiteDatabase/TestSqlLiteDatabase.csproj b/TestSqlLiteDatabase/TestSqlLiteDatabase.csproj index be30cb2..cd44017 100644 --- a/TestSqlLiteDatabase/TestSqlLiteDatabase.csproj +++ b/TestSqlLiteDatabase/TestSqlLiteDatabase.csproj @@ -1,7 +1,7 @@ - net5 + net8.0 false diff --git a/global.json b/global.json new file mode 100644 index 0000000..e23437b --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "7.0.100", + "rollForward": "latestMajor" + } +}