内存中的 Sqlite - TransactionScope - 已检测到环境事务

Posted

技术标签:

【中文标题】内存中的 Sqlite - TransactionScope - 已检测到环境事务【英文标题】:Sqlite in-memory - TransactionScope - ambient transaction has been detected 【发布时间】:2020-05-21 15:39:30 【问题描述】:

我遇到了异常

Microsoft.EntityFrameworkCore.Database.Transaction.AmbientTransactionWarning: 检测到环境事务

但我不知道如何解决此异常。当我在TransactionScope 中调用SaveChanges() 时,我的代码失败了。可能是因为TransactionScope本身在SQLite context事务里面,但是这个问题怎么处理呢?

我要测试的方法

它使用 TransactionScope,因为我想确定,当我与他一起操作时,加载的对象(Request 类)不会改变。

public void Handle(Create cmd)

    // Wrap via transaction
    using (var transaction = new TransactionScope(TransactionScopeOption.Required,
        new TransactionOptions  IsolationLevel = IsolationLevel.RepeatableRead ))
    

        // Load request object from repository.
        Request request = _repository.Find(cmd.RequestId);

        // Create detail object
        var detail = new Detail(requestId: cmd.RequestId);

        // Add created detail object into Request object
        request.CreateDetail(detail);

        // Save request into repository.
        _repository.SaveChanges();                // <--- HERE IS THE EXCEPTION THROWN

        // Commit transaction
        transaction.Complete();
    

我要执行的测试方法 它使用 SQLite 内存中的关系数据库,因为 TransactionScope 不支持非关系数据库。

[Fact]
public void Create()

    // Arrange
    // --------------------------------------------------------------------- 

    var request = new Request();

    using (var connection = new SqliteConnection("DataSource=:memory:"))
    
        connection.Open();

        var options = new DbContextOptionsBuilder<SamplesContext>()
            .UseSqlite(connection)
            .Options;

        using (var context = new SamplesContext(options))
        
            context.Database.EnsureCreated();

            var service = SetupDetailService(context);

            context.Request.Add(request);
            context.SaveChanges();

            var storedRequest = context.Request.FirstOrDefault();

            var createCommand = new Create(requestId: Convert.ToInt32(storedRequest.Id));

            // Act
            // --------------------------------------------------------------------- 
            service.Handle(createCommand);        // <--- HERE IS THE EXCEPTION THROWN
            context.SaveChanges();

            // Assert
            // ----------------------------------------------------------------------
            // ...

        ;
    ;


【问题讨论】:

【参考方案1】:

我阻止了错误以运行测试:

var builder = new DbContextOptionsBuilder<EpsOnlineDbContext>();
DbConnection connection = new SqliteConnection("DataSource=:memory:");
connection.OpenAsync().GetAwaiter().GetResult();
builder.UseSqlite(connection);
builder.ConfigureWarnings(x => x.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning));

【讨论】:

如果您使用的是 EF Core 3 或更高版本,则此方法有效。如果您使用的是 EF Core 2.x,上面的代码将清除环境警告,但随后您将收到 System.NotSupportedException 消息。这是 2.x 中未修复的错误,解决它的唯一方法是切换到在内存 DB 中使用 EF(不理想)或迁移到 EF Core 3 或更高版本。请参阅github.com/dotnet/efcore/issues/15413 了解更多信息。 根据我的经验,builder.ConfigureWarnings() 应该在 builder.UseSqlite() 之前。

以上是关于内存中的 Sqlite - TransactionScope - 已检测到环境事务的主要内容,如果未能解决你的问题,请参考以下文章

SQLIte Transaction

FireDAC 下的 Sqlite [7] - 备份优化事务(Transaction)/

在 SQLite + BEGIN TRANSACTION 中更新更快

错误:未定义不是 ReactNative 中的对象(评估“db.transaction”)

如何使用命令行工具 sqlite3 浏览内存中的 SQLite 数据库

在 React Native 中的 SQLite 上传递两个参数时无法获取数据