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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
49 changes: 47 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ jobs:
exit 2
}
exit 0
- name: 'Run Tests: EFCore.Jet.FunctionalTests (Shard 3 - Non-Query)'
- name: 'Run Tests: EFCore.Jet.FunctionalTests (Shard 3 - Non-Query, excluding CompiledModel)'
if: always() && env.skipTests != 'true'
shell: pwsh
run: |
Expand All @@ -495,7 +495,52 @@ jobs:
}

$env:EFCoreJet_DefaultConnection = '${{ env.defaultConnection }}'
& '${{ env.dotnetExecutable }}' test .\test\EFCore.Jet.FunctionalTests --configuration '${{ env.buildConfiguration }}' -p:FixedTestOrder=${{ env.deterministicTests }} --logger trx --blame-hang-timeout 3m --results-directory $shardDir --filter "FullyQualifiedName!~.FunctionalTests.Query."
& '${{ env.dotnetExecutable }}' test .\test\EFCore.Jet.FunctionalTests --configuration '${{ env.buildConfiguration }}' -p:FixedTestOrder=${{ env.deterministicTests }} --logger trx --blame-hang-timeout 3m --results-directory $shardDir --filter "FullyQualifiedName!~.FunctionalTests.Query.&FullyQualifiedName!~CompiledModel"

#
# Check for test runner crashes:
#

$currentTestRunTrx = Get-ChildItem $shardDir -Filter '*.trx' | Sort-Object LastWriteTime | Select-Object -Last 1
if ($null -eq $currentTestRunTrx) {
echo 'Test runner log file is missing.'
exit 3
}

$currentTestRunDir = Join-Path $shardDir $currentTestRunTrx.BaseName
if (Test-Path $currentTestRunDir) {
if ($null -ne (Get-ChildItem $currentTestRunDir -Filter 'Sequence_*' -Recurse)) {
# Split string because searching the log for that phrase should only show actual crashes and not this line.
echo ('Test runner cras' + 'hed.')
continue
}
}

echo 'Test runner ran until the end.'
break
}

$establishedGreenTestsFilePath = ".\test\EFCore.Jet.FunctionalTests\GreenTests\ace_${{ matrix.aceVersion }}_$('${{ matrix.dataAccessProviderType }}'.Replace(' ', '').ToLowerInvariant())_${{ matrix.aceArchitecture }}.txt"
$failIfKeepsCrashing = Test-Path $establishedGreenTestsFilePath

if ($i -ge 3 -and $failIfKeepsCrashing) {
echo 'Test runner keeps crashing.'
exit 2
}
exit 0
- name: 'Run Tests: EFCore.Jet.FunctionalTests (Shard 4 - CompiledModel)'
if: always() && env.skipTests != 'true'
shell: pwsh
run: |
$shardDir = '.\test\EFCore.Jet.FunctionalTests\TestResults\shard4'

for ($i = 0; $i -lt 3; $i++) {
if (Test-Path $shardDir -PathType Container) {
Get-ChildItem $shardDir | Remove-Item -Recurse -Force
}

$env:EFCoreJet_DefaultConnection = '${{ env.defaultConnection }}'
& '${{ env.dotnetExecutable }}' test .\test\EFCore.Jet.FunctionalTests --configuration '${{ env.buildConfiguration }}' -p:FixedTestOrder=${{ env.deterministicTests }} --logger trx --blame-hang-timeout 3m --results-directory $shardDir --filter "FullyQualifiedName~CompiledModel"

#
# Check for test runner crashes:
Expand Down
49 changes: 47 additions & 2 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ jobs:
exit 2
}
exit 0
- name: 'Run Tests: EFCore.Jet.FunctionalTests (Shard 3 - Non-Query)'
- name: 'Run Tests: EFCore.Jet.FunctionalTests (Shard 3 - Non-Query, excluding CompiledModel)'
if: always() && env.skipTests != 'true'
shell: pwsh
run: |
Expand All @@ -504,7 +504,52 @@ jobs:
}

$env:EFCoreJet_DefaultConnection = '${{ env.defaultConnection }}'
& '${{ env.dotnetExecutable }}' test .\test\EFCore.Jet.FunctionalTests --configuration '${{ env.buildConfiguration }}' -p:FixedTestOrder=${{ env.deterministicTests }} --logger trx --blame-hang-timeout 3m --results-directory $shardDir --filter "FullyQualifiedName!~.FunctionalTests.Query."
& '${{ env.dotnetExecutable }}' test .\test\EFCore.Jet.FunctionalTests --configuration '${{ env.buildConfiguration }}' -p:FixedTestOrder=${{ env.deterministicTests }} --logger trx --blame-hang-timeout 3m --results-directory $shardDir --filter "FullyQualifiedName!~.FunctionalTests.Query.&FullyQualifiedName!~CompiledModel"

#
# Check for test runner crashes:
#

$currentTestRunTrx = Get-ChildItem $shardDir -Filter '*.trx' | Sort-Object LastWriteTime | Select-Object -Last 1
if ($null -eq $currentTestRunTrx) {
echo 'Test runner log file is missing.'
exit 3
}

$currentTestRunDir = Join-Path $shardDir $currentTestRunTrx.BaseName
if (Test-Path $currentTestRunDir) {
if ($null -ne (Get-ChildItem $currentTestRunDir -Filter 'Sequence_*' -Recurse)) {
# Split string because searching the log for that phrase should only show actual crashes and not this line.
echo ('Test runner cras' + 'hed.')
continue
}
}

echo 'Test runner ran until the end.'
break
}

$establishedGreenTestsFilePath = ".\test\EFCore.Jet.FunctionalTests\GreenTests\ace_${{ matrix.aceVersion }}_$('${{ matrix.dataAccessProviderType }}'.Replace(' ', '').ToLowerInvariant())_${{ matrix.aceArchitecture }}.txt"
$failIfKeepsCrashing = Test-Path $establishedGreenTestsFilePath

if ($i -ge 3 -and $failIfKeepsCrashing) {
echo 'Test runner keeps crashing.'
exit 2
}
exit 0
- name: 'Run Tests: EFCore.Jet.FunctionalTests (Shard 4 - CompiledModel)'
if: always() && env.skipTests != 'true'
shell: pwsh
run: |
$shardDir = '.\test\EFCore.Jet.FunctionalTests\TestResults\shard4'

for ($i = 0; $i -lt 3; $i++) {
if (Test-Path $shardDir -PathType Container) {
Get-ChildItem $shardDir | Remove-Item -Recurse -Force
}

$env:EFCoreJet_DefaultConnection = '${{ env.defaultConnection }}'
& '${{ env.dotnetExecutable }}' test .\test\EFCore.Jet.FunctionalTests --configuration '${{ env.buildConfiguration }}' -p:FixedTestOrder=${{ env.deterministicTests }} --logger trx --blame-hang-timeout 3m --results-directory $shardDir --filter "FullyQualifiedName~CompiledModel"

#
# Check for test runner crashes:
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ It is kept in sync with `CLAUDE.md`; if you change guidance in one, check whethe

EntityFrameworkCore.Jet is an EF Core provider for Microsoft Jet/ACE databases (Microsoft Access `.mdb`/`.accdb` files). The **Jet** provider runs **Windows only** and bridges EF Core to the Access database engine via either ODBC or OLE DB. Alongside it, **LibRed** (also in this repo, on `master`) is a from-scratch managed engine that reads/writes the file format directly and is **cross-platform** — see the LibRed section below.

Current version: `11.0.0-alpha.1` (`Version.props`) targeting EF Core 11 and `net11.0`; `global.json` pins an 11.0.100 preview SDK with `rollForward: latestFeature`. The test projects use **xunit v3**.
Current version: `11.0.0-alpha.1` (`Version.props`) targeting EF Core 11 and `net11.0`; `global.json` pins the 11.0.100 RC1 SDK with `rollForward: latestFeature`. The test projects use **xunit v3**.

### Which layer am I touching?

Expand Down Expand Up @@ -167,7 +167,7 @@ shared type subclassing or branching — see `LibRedConventionSetBuilder`, `LibR
`LibRedCodeGenerator`, `LibRedDesignTimeServices`. The types below are named where they live today:
- `JetServiceCollectionExtensions.AddEntityFrameworkJet()` registers all provider services.
- `JetQuerySqlGenerator` extends `QuerySqlGenerator` to produce Jet-compatible SQL — converts `CAST` to Jet VBA functions (`CBOOL`, `CINT`, `CLNG`, etc.), handles boolean/numeric null semantics.
- `JetQueryTranslationPostprocessor` applies Jet-specific query rewrites in this order: skip/take transformation → base postprocessing → **append the query's last identifier column to `ORDER BY`** (deterministic tie-breaking, only when the query already orders) → optional millisecond support → ORDER BY lifting. `JetSkipTakePostprocessor` emulates `SKIP`/`OFFSET` since Jet only supports `SELECT TOP n`. Note it reaches `SelectExpression._identifier` by reflection, so an EF Core update can break it at runtime rather than at compile time.
- `JetQueryTranslationPostprocessor` applies Jet-specific query rewrites in this order: skip/take transformation → base postprocessing → ORDER BY lifting. `JetSkipTakePostprocessor` emulates `SKIP`/`OFFSET` since Jet only supports `SELECT TOP n`. LibRed's copy runs the same two steps but gates both on `LibRedSqlMode.Compatible`.
- `JetMigrationsSqlGenerator` generates DDL for Access. It **does** emit `ALTER TABLE … ALTER COLUMN` (Jet's form folds the default value into the `ALTER COLUMN` rather than taking a separate operation); constraint support is still limited.
- `JetHistoryRepository` implements migration locking via a `__EFMigrationsLock` table with `LockReleaseBehavior.Explicit`.
- `JetRelationalConnection` creates an "empty" (masterless) connection for database creation/drop operations.
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

EntityFrameworkCore.Jet is an EF Core provider for Microsoft Jet/ACE databases (Microsoft Access `.mdb`/`.accdb` files). The **Jet** provider runs **Windows only** and bridges EF Core to the Access database engine via either ODBC or OLE DB. Alongside it, **LibRed** (also in this repo, on `master`) is a from-scratch managed engine that reads/writes the file format directly and is **cross-platform** — see the LibRed section below.

Current version: `11.0.0-alpha.1` (`Version.props`) targeting EF Core 11 and `net11.0`; `global.json` pins an 11.0.100 preview SDK with `rollForward: latestFeature`. The test projects use **xunit v3**.
Current version: `11.0.0-alpha.1` (`Version.props`) targeting EF Core 11 and `net11.0`; `global.json` pins the 11.0.100 RC1 SDK with `rollForward: latestFeature`. The test projects use **xunit v3**.

### Which layer am I touching?

Expand Down Expand Up @@ -179,7 +179,7 @@ shared type subclassing or branching — see `LibRedConventionSetBuilder`, `LibR
`LibRedCodeGenerator`, `LibRedDesignTimeServices`. The types below are named where they live today:
- `JetServiceCollectionExtensions.AddEntityFrameworkJet()` registers all provider services.
- `JetQuerySqlGenerator` extends `QuerySqlGenerator` to produce Jet-compatible SQL — converts `CAST` to Jet VBA functions (`CBOOL`, `CINT`, `CLNG`, etc.), handles boolean/numeric null semantics.
- `JetQueryTranslationPostprocessor` applies Jet-specific query rewrites in this order: skip/take transformation → base postprocessing → **append the query's last identifier column to `ORDER BY`** (deterministic tie-breaking, only when the query already orders) → optional millisecond support → ORDER BY lifting. `JetSkipTakePostprocessor` emulates `SKIP`/`OFFSET` since Jet only supports `SELECT TOP n`. Note it reaches `SelectExpression._identifier` by reflection, so an EF Core update can break it at runtime rather than at compile time.
- `JetQueryTranslationPostprocessor` applies Jet-specific query rewrites in this order: skip/take transformation → base postprocessing → ORDER BY lifting. `JetSkipTakePostprocessor` emulates `SKIP`/`OFFSET` since Jet only supports `SELECT TOP n`. LibRed's copy runs the same two steps but gates both on `LibRedSqlMode.Compatible`.
- `JetMigrationsSqlGenerator` generates DDL for Access. It **does** emit `ALTER TABLE … ALTER COLUMN` (Jet's form folds the default value into the `ALTER COLUMN` rather than taking a separate operation); constraint support is still limited.
- `JetHistoryRepository` implements migration locking via a `__EFMigrationsLock` table with `LockReleaseBehavior.Explicit`.
- `JetRelationalConnection` creates an "empty" (masterless) connection for database creation/drop operations.
Expand Down
26 changes: 13 additions & 13 deletions Dependencies.targets
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<DotNetVersion>[11.0.0-preview.7.26381.103]</DotNetVersion>
<EFCoreVersion>[11.0.0-preview.7.26381.103]</EFCoreVersion>
<MSLibVersion>[11.0.0-preview.7.26381.103]</MSLibVersion>
<DotNetVersion>[11.0.0-rc.1.26425.128]</DotNetVersion>
<EFCoreVersion>[11.0.0-rc.1.26425.128]</EFCoreVersion>
<MSLibVersion>[11.0.0-rc.1.26425.128]</MSLibVersion>
</PropertyGroup>

<ItemGroup>
Expand All @@ -12,7 +12,7 @@
<PackageReference Update="Microsoft.EntityFrameworkCore" Version="$(EFCoreVersion)" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational" Version="$(EFCoreVersion)" />
<PackageReference Update="DotNetAnalyzers.DocumentationAnalyzers" Version="1.0.0-beta.59" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.300" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="11.0.100-rc.1.26425.128" />

<!-- EFCore.Jet -->
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="$(MSLibVersion)" />
Expand All @@ -25,27 +25,27 @@
<PackageReference Update="Microsoft.EntityFrameworkCore.Design" Version="$(EFCoreVersion)" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Relational.Specification.Tests" Version="$(EFCoreVersion)" />
<PackageReference Update="Microsoft.Extensions.Logging.Console" Version="$(MSLibVersion)" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="18.10.0" />
<!-- Centralised so the three test projects that use it cannot drift apart again; they previously
carried inline 18.7.1 / 18.7.1 / 18.4.0. -->
<PackageReference Update="Microsoft.Build.Tasks.Core" Version="18.7.1" />
<PackageReference Update="MSTest.TestAdapter" Version="4.2.3" />
<PackageReference Update="MSTest.TestFramework" Version="4.2.3" />
<PackageReference Update="Microsoft.Build.Tasks.Core" Version="18.9.6" />
<PackageReference Update="MSTest.TestAdapter" Version="4.4.0" />
<PackageReference Update="MSTest.TestFramework" Version="4.4.0" />
<PackageReference Update="coverlet.collector" Version="10.0.1" />

<!-- EFCore.Jet.FunctionalTests -->
<!-- xunit v3. Not a preference: EF 11's Relational.Specification.Tests depends on
Microsoft.DotNet.XUnitV3Extensions, so inheriting from its test bases requires v3. Pinned to the
same build EF resolves, so the two cannot disagree about which xunit is in the graph. -->
<PackageReference Update="xunit.v3.core" Version="4.0.0-pre.108" />
<PackageReference Update="xunit.v3.assert" Version="4.0.0-pre.108" />
<PackageReference Update="xunit.v3.extensibility.core" Version="4.0.0-pre.108" />
<PackageReference Update="xunit.runner.visualstudio" Version="3.1.5" />
<PackageReference Update="xunit.v3.core" Version="4.0.0" />
<PackageReference Update="xunit.v3.assert" Version="4.0.0" />
<PackageReference Update="xunit.v3.extensibility.core" Version="4.0.0" />
<PackageReference Update="xunit.runner.visualstudio" Version="4.0.0" />
<PackageReference Update="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MSLibVersion)" />
<PackageReference Update="NetTopologySuite" Version="2.6.0" />

<!-- LibRed.Benchmarks -->
<PackageReference Update="BenchmarkDotNet" Version="0.15.2" />
<PackageReference Update="BenchmarkDotNet" Version="0.15.8" />

<!-- EFCore.Jet.Tests -->
<PackageReference Update="Microsoft.Extensions.DependencyModel" Version="$(MSLibVersion)" />
Expand Down
2 changes: 1 addition & 1 deletion Version.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
-->
<VersionPrefix>11.0.0</VersionPrefix>
<PreReleaseVersionLabel>alpha</PreReleaseVersionLabel>
<PreReleaseVersionIteration>1</PreReleaseVersionIteration>
<PreReleaseVersionIteration>2</PreReleaseVersionIteration>

<!--
The following properties will automatically be set by CI builds when appropriate:
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "11.0.100-preview.7.26381.103",
"version": "11.0.100-rc.1.26425.128",
"allowPrerelease": true,
"rollForward": "latestFeature"
}
Expand Down
26 changes: 0 additions & 26 deletions src/EFCore.Jet/Query/Internal/JetQueryTranslationPostprocessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ namespace EntityFrameworkCore.Jet.Query.Internal
{
public class JetQueryTranslationPostprocessor : RelationalQueryTranslationPostprocessor
{
private static readonly FieldInfo SelectExpressionIdentifierField = typeof(SelectExpression).GetField(
"_identifier",
BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new InvalidOperationException("Could not find SelectExpression._identifier.");

private readonly IRelationalTypeMappingSource _relationalTypeMappingSource;
private readonly JetLiftOrderByPostprocessor _liftOrderByPostprocessor;
private readonly JetSkipTakePostprocessor _skipTakePostprocessor;
Expand All @@ -34,30 +29,9 @@ public override Expression Process(Expression query)

query = base.Process(query);

var identifiers = GetIdentifiers(query);

if (identifiers.Count > 0
&& query is ShapedQueryExpression { QueryExpression: SelectExpression selectExpression }
&& !selectExpression.Orderings.Any(
ordering => ordering.Expression.Equals(identifiers[^1].Column)) && selectExpression.Orderings.Any())
{
selectExpression.AppendOrdering(
new OrderingExpression(identifiers[^1].Column, ascending: true));
}

query = _liftOrderByPostprocessor.Process(query);

return query;
}

private static IReadOnlyList<(ColumnExpression Column, ValueComparer Comparer)> GetIdentifiers(Expression query)
{
if (query is not ShapedQueryExpression { QueryExpression: SelectExpression selectExpression })
{
return [];
}

return (IReadOnlyList<(ColumnExpression Column, ValueComparer Comparer)>)SelectExpressionIdentifierField.GetValue(selectExpression)!;
}
}
}
Loading
Loading