Skip to content

Commit 6a8ccd2

Browse files
authored
Fix transactions issues (#129)
* Fix active transaction support and disposed handling. * Add transaction baseline tests. * Fix transaction tests.
1 parent 3b6fee5 commit 6a8ccd2

File tree

3 files changed

+149
-27
lines changed

3 files changed

+149
-27
lines changed

src/EFCore.Jet.Data/JetTransaction.cs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
using System.Data;
1+
using System;
2+
using System.Data;
23
using System.Data.Common;
4+
using System.Threading;
5+
using System.Threading.Tasks;
36

47
namespace EntityFrameworkCore.Jet.Data
58
{
69
internal class JetTransaction : DbTransaction
710
{
8-
private readonly JetConnection _connection;
11+
private JetConnection _connection;
12+
private bool _disposed;
913

1014
internal virtual DbTransaction WrappedTransaction { get; }
1115

@@ -22,8 +26,12 @@ public JetTransaction(JetConnection connection, IsolationLevel isolationLevel)
2226

2327
public override void Commit()
2428
{
29+
if (_disposed)
30+
throw new ObjectDisposedException(nameof(JetTransaction));
31+
2532
LogHelper.ShowCommandHeader("--- Commit");
2633
WrappedTransaction.Commit();
34+
2735
_connection.ActiveTransaction = null;
2836
}
2937

@@ -35,9 +43,63 @@ public override IsolationLevel IsolationLevel
3543

3644
public override void Rollback()
3745
{
46+
if (_disposed)
47+
throw new ObjectDisposedException(nameof(JetTransaction));
48+
3849
LogHelper.ShowCommandHeader("^^^ Rollback");
3950
WrappedTransaction.Rollback();
51+
4052
_connection.ActiveTransaction = null;
4153
}
54+
55+
protected override void Dispose(bool disposing)
56+
{
57+
try
58+
{
59+
if (disposing)
60+
{
61+
if (_connection?.ActiveTransaction == this)
62+
{
63+
if (Connection.State == ConnectionState.Open)
64+
{
65+
Rollback();
66+
}
67+
68+
_connection.ActiveTransaction = null;
69+
}
70+
}
71+
}
72+
finally
73+
{
74+
_disposed = true;
75+
_connection = null;
76+
77+
base.Dispose(disposing);
78+
}
79+
}
80+
81+
public override Task CommitAsync(CancellationToken cancellationToken = new CancellationToken())
82+
{
83+
if (_disposed)
84+
throw new ObjectDisposedException(nameof(JetTransaction));
85+
86+
return base.CommitAsync(cancellationToken);
87+
}
88+
89+
public override ValueTask DisposeAsync()
90+
{
91+
if (_disposed)
92+
throw new ObjectDisposedException(nameof(JetTransaction));
93+
94+
return base.DisposeAsync();
95+
}
96+
97+
public override Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken())
98+
{
99+
if (_disposed)
100+
throw new ObjectDisposedException(nameof(JetTransaction));
101+
102+
return base.RollbackAsync(cancellationToken);
103+
}
42104
}
43105
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using Microsoft.VisualStudio.TestTools.UnitTesting;
2+
3+
namespace EntityFrameworkCore.Jet.Data.Tests
4+
{
5+
[TestClass]
6+
public class TransactionTest
7+
{
8+
private const string StoreName = nameof(TransactionTest) + ".accdb";
9+
10+
private JetConnection _connection;
11+
12+
[TestInitialize]
13+
public void Setup()
14+
{
15+
_connection = Helpers.CreateAndOpenDatabase(StoreName);
16+
17+
var script = @"
18+
CREATE TABLE `Cookies` (
19+
`CookieId` counter NOT NULL,
20+
`Name` text NULL,
21+
CONSTRAINT `PK_Cookies` PRIMARY KEY (`CookieId`)
22+
);
23+
24+
INSERT INTO `Cookies` (`Name`) VALUES ('Basic');
25+
INSERT INTO `Cookies` (`Name`) VALUES ('Chocolate Chip');
26+
";
27+
28+
Helpers.ExecuteScript(_connection, script);
29+
}
30+
31+
[TestCleanup]
32+
public void TearDown()
33+
{
34+
_connection?.Close();
35+
Helpers.DeleteDatabase(StoreName);
36+
}
37+
38+
[TestMethod]
39+
public void Delete_rollback_implicit()
40+
{
41+
using (var transaction = _connection.BeginTransaction())
42+
{
43+
using var deleteCommand = _connection.CreateCommand();
44+
deleteCommand.CommandText = @"delete * from `Cookies` where `Name` = 'Basic'";
45+
deleteCommand.Transaction = transaction;
46+
var affected = deleteCommand.ExecuteNonQuery();
47+
48+
Assert.AreEqual(1, affected);
49+
}
50+
51+
using var verifyCommand = _connection.CreateCommand();
52+
verifyCommand.CommandText = @"select count(*) as `Count` from `Cookies`";
53+
var count = verifyCommand.ExecuteScalar();
54+
55+
Assert.AreEqual(2, count);
56+
}
57+
58+
[TestMethod]
59+
public void Delete_rollback_explicit()
60+
{
61+
using (var transaction = _connection.BeginTransaction())
62+
{
63+
using var deleteCommand = _connection.CreateCommand();
64+
deleteCommand.CommandText = @"delete * from `Cookies` where `Name` = 'Basic'";
65+
deleteCommand.Transaction = transaction;
66+
var affected = deleteCommand.ExecuteNonQuery();
67+
68+
transaction.Rollback();
69+
70+
Assert.AreEqual(1, affected);
71+
}
72+
73+
using var verifyCommand = _connection.CreateCommand();
74+
verifyCommand.CommandText = @"select count(*) as `Count` from `Cookies`";
75+
var count = verifyCommand.ExecuteScalar();
76+
77+
Assert.AreEqual(2, count);
78+
}
79+
}
80+
}

test/EFCore.Jet.FunctionalTests/TransactionJettest.cs renamed to test/EFCore.Jet.FunctionalTests/TransactionJetTest.cs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3-
using EntityFrameworkCore.Jet.Data;
43
using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
54
using EntityFrameworkCore.Jet.Infrastructure;
6-
using Microsoft.EntityFrameworkCore.Infrastructure;
75
using EntityFrameworkCore.Jet.Storage.Internal;
86
using Microsoft.EntityFrameworkCore;
97
using Microsoft.EntityFrameworkCore.TestUtilities;
@@ -17,9 +15,10 @@ public TransactionJetTest(TransactionJetFixture fixture)
1715
{
1816
}
1917

20-
protected override bool SnapshotSupported => true;
21-
22-
protected override bool AmbientTransactionsSupported => true;
18+
protected override bool SnapshotSupported => false;
19+
protected override bool AmbientTransactionsSupported => false;
20+
protected override bool DirtyReadsOccur => false;
21+
protected override bool SavepointsSupported => false;
2322

2423
protected override DbContext CreateContextWithConnectionString()
2524
{
@@ -37,26 +36,7 @@ protected override DbContext CreateContextWithConnectionString()
3736
public class TransactionJetFixture : TransactionFixtureBase
3837
{
3938
protected override ITestStoreFactory TestStoreFactory => JetTestStoreFactory.Instance;
40-
41-
protected override void Seed(PoolableDbContext context)
42-
{
43-
base.Seed(context);
44-
45-
context.Database.ExecuteSqlRaw("ALTER DATABASE [" + StoreName + "] SET ALLOW_SNAPSHOT_ISOLATION ON");
46-
context.Database.ExecuteSqlRaw("ALTER DATABASE [" + StoreName + "] SET READ_COMMITTED_SNAPSHOT ON");
47-
}
48-
49-
public override void Reseed()
50-
{
51-
using (var context = CreateContext())
52-
{
53-
context.Set<TransactionCustomer>().RemoveRange(context.Set<TransactionCustomer>());
54-
context.SaveChanges();
55-
56-
base.Seed(context);
57-
}
58-
}
59-
39+
6040
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
6141
{
6242
new JetDbContextOptionsBuilder(

0 commit comments

Comments
 (0)