Fix transactions issues (#129)

* Fix active transaction support and disposed handling.

* Add transaction baseline tests.

* Fix transaction tests.
5.0-servicing
Laurents Meyer 4 years ago committed by GitHub
parent 3b6fee5a89
commit 6a8ccd27de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,11 +1,15 @@
using System.Data;
using System;
using System.Data;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
namespace EntityFrameworkCore.Jet.Data
{
internal class JetTransaction : DbTransaction
{
private readonly JetConnection _connection;
private JetConnection _connection;
private bool _disposed;
internal virtual DbTransaction WrappedTransaction { get; }
@ -22,8 +26,12 @@ namespace EntityFrameworkCore.Jet.Data
public override void Commit()
{
if (_disposed)
throw new ObjectDisposedException(nameof(JetTransaction));
LogHelper.ShowCommandHeader("--- Commit");
WrappedTransaction.Commit();
_connection.ActiveTransaction = null;
}
@ -35,9 +43,63 @@ namespace EntityFrameworkCore.Jet.Data
public override void Rollback()
{
if (_disposed)
throw new ObjectDisposedException(nameof(JetTransaction));
LogHelper.ShowCommandHeader("^^^ Rollback");
WrappedTransaction.Rollback();
_connection.ActiveTransaction = null;
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_connection?.ActiveTransaction == this)
{
if (Connection.State == ConnectionState.Open)
{
Rollback();
}
_connection.ActiveTransaction = null;
}
}
}
finally
{
_disposed = true;
_connection = null;
base.Dispose(disposing);
}
}
public override Task CommitAsync(CancellationToken cancellationToken = new CancellationToken())
{
if (_disposed)
throw new ObjectDisposedException(nameof(JetTransaction));
return base.CommitAsync(cancellationToken);
}
public override ValueTask DisposeAsync()
{
if (_disposed)
throw new ObjectDisposedException(nameof(JetTransaction));
return base.DisposeAsync();
}
public override Task RollbackAsync(CancellationToken cancellationToken = new CancellationToken())
{
if (_disposed)
throw new ObjectDisposedException(nameof(JetTransaction));
return base.RollbackAsync(cancellationToken);
}
}
}

@ -0,0 +1,80 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace EntityFrameworkCore.Jet.Data.Tests
{
[TestClass]
public class TransactionTest
{
private const string StoreName = nameof(TransactionTest) + ".accdb";
private JetConnection _connection;
[TestInitialize]
public void Setup()
{
_connection = Helpers.CreateAndOpenDatabase(StoreName);
var script = @"
CREATE TABLE `Cookies` (
`CookieId` counter NOT NULL,
`Name` text NULL,
CONSTRAINT `PK_Cookies` PRIMARY KEY (`CookieId`)
);
INSERT INTO `Cookies` (`Name`) VALUES ('Basic');
INSERT INTO `Cookies` (`Name`) VALUES ('Chocolate Chip');
";
Helpers.ExecuteScript(_connection, script);
}
[TestCleanup]
public void TearDown()
{
_connection?.Close();
Helpers.DeleteDatabase(StoreName);
}
[TestMethod]
public void Delete_rollback_implicit()
{
using (var transaction = _connection.BeginTransaction())
{
using var deleteCommand = _connection.CreateCommand();
deleteCommand.CommandText = @"delete * from `Cookies` where `Name` = 'Basic'";
deleteCommand.Transaction = transaction;
var affected = deleteCommand.ExecuteNonQuery();
Assert.AreEqual(1, affected);
}
using var verifyCommand = _connection.CreateCommand();
verifyCommand.CommandText = @"select count(*) as `Count` from `Cookies`";
var count = verifyCommand.ExecuteScalar();
Assert.AreEqual(2, count);
}
[TestMethod]
public void Delete_rollback_explicit()
{
using (var transaction = _connection.BeginTransaction())
{
using var deleteCommand = _connection.CreateCommand();
deleteCommand.CommandText = @"delete * from `Cookies` where `Name` = 'Basic'";
deleteCommand.Transaction = transaction;
var affected = deleteCommand.ExecuteNonQuery();
transaction.Rollback();
Assert.AreEqual(1, affected);
}
using var verifyCommand = _connection.CreateCommand();
verifyCommand.CommandText = @"select count(*) as `Count` from `Cookies`";
var count = verifyCommand.ExecuteScalar();
Assert.AreEqual(2, count);
}
}
}

@ -1,9 +1,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using EntityFrameworkCore.Jet.Data;
using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
using EntityFrameworkCore.Jet.Infrastructure;
using Microsoft.EntityFrameworkCore.Infrastructure;
using EntityFrameworkCore.Jet.Storage.Internal;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestUtilities;
@ -17,9 +15,10 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
{
}
protected override bool SnapshotSupported => true;
protected override bool AmbientTransactionsSupported => true;
protected override bool SnapshotSupported => false;
protected override bool AmbientTransactionsSupported => false;
protected override bool DirtyReadsOccur => false;
protected override bool SavepointsSupported => false;
protected override DbContext CreateContextWithConnectionString()
{
@ -37,26 +36,7 @@ namespace EntityFrameworkCore.Jet.FunctionalTests
public class TransactionJetFixture : TransactionFixtureBase
{
protected override ITestStoreFactory TestStoreFactory => JetTestStoreFactory.Instance;
protected override void Seed(PoolableDbContext context)
{
base.Seed(context);
context.Database.ExecuteSqlRaw("ALTER DATABASE [" + StoreName + "] SET ALLOW_SNAPSHOT_ISOLATION ON");
context.Database.ExecuteSqlRaw("ALTER DATABASE [" + StoreName + "] SET READ_COMMITTED_SNAPSHOT ON");
}
public override void Reseed()
{
using (var context = CreateContext())
{
context.Set<TransactionCustomer>().RemoveRange(context.Set<TransactionCustomer>());
context.SaveChanges();
base.Seed(context);
}
}
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
{
new JetDbContextOptionsBuilder(
Loading…
Cancel
Save