繁体   English   中英

在单独的项目中的EF DbContext之间共享事务

[英]Sharing a transaction between EF DbContext in separated projects

我要做的是使用Entity Framework为微服务环境编写一些集成测试,在其中我需要隔离每个测试方案的事务,以使测试在操作数据库方面不会互相影响。

然后,在运行测试之后,我想检查数据库以查看工作是否正确完成。

到目前为止,我要做的是创建两个基础类,如下所示。 (我正在使用xunit作为测试框架)

public class DatabaseInitializer : IDisposable
{
    public readonly MyContext DatabaseContext;

    public DatabaseInitializer()
    {
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables();

        var configuration = builder.Build();
        DatabaseContext = new MyContext(configuration.GetConnectionString("SqlConnectionString"));
        DatabaseContext.Database.EnsureCreated();
    }

    public void Dispose()
    {
        DatabaseContext.Database.EnsureDeleted();
    }
}

public class TransactionIsolator : IClassFixture<DatabaseInitializer> , IDisposable
{
    protected readonly TransactionScope TransactionScope;
    protected readonly MyContext DatabaseContext;

    public TransactionIsolator(DatabaseInitializer dbInitializer)
    {
        DatabaseContext = dbInitializer.DatabaseContext;
        TransactionScope = new TransactionScope(TransactionScopeOption.RequiresNew);
    }

    public void Dispose()
    {
        TransactionScope.Dispose();
    }
}

在这里,第一个( DatabaseInitializer )将确保在测试套件的第一个运行时创建所需的数据库,然后在处置它时将其删除。

然后,第二个类将创建一个事务,并在测试完成后回滚更改。 因此,当我编写如下所示的两个测试时,它将正确隔离每个运行的测试用例对数据库的访问。

public class ExampleTests : TransactionIsolator
{
    public ExampleTests(DatabaseInitializer dbInitializer) : base(dbInitializer)
    {
    }

    [Fact]
    void example_test()
    {
        DatabaseContext.Indexes.Should().BeEmpty();
        DatabaseContext.Indexes.Add(new Indexes {PublisherId = "123", LastIndex = new byte[] {0x12, 0x13}});
        DatabaseContext.SaveChanges();
        DatabaseContext.Indexes.Should().NotBeEmpty();
    }

    [Fact]
    void example_test_2()
    {
        DatabaseContext.Indexes.Should().BeEmpty();
        DatabaseContext.Indexes.Add(new Indexes {PublisherId = "321", LastIndex = new byte[] {0x12, 0x13}});
        DatabaseContext.SaveChanges();
        DatabaseContext.Indexes.Should().NotBeEmpty();
    }
}

这些都将通过,一切都很好,除非我无法访问微服务来设置其事务,或者至少我不知道是否有可能。 我的意思是,如果我可以配置或发信号通知微服务以从该应用程序的范围之外为其数据库使用事务,那将非常棒。

除此之外,如果不可能的话,在这种情况下您可以建议使用什么呢?我想为每个测试方案获得隔离,并且仍然能够运行它们是并行的。

对于大多数提供商来说,这“行之有效”。 TransactionScope设置静态属性Transaction.Current ,并且ADO.Net Provider可以(并且通常这样做)参与环境事务。 请参阅: 使用事务范围实现隐式事务

至少对于SQL Server中,如果您的测试使用相同的连接字符串,并且不同时运行,他们将采用相同的SqlConnection且不需要推广分布式事务。 由于SQLClient连接池是按事务划分的,因此对池的相同请求(ConnectionString,Transaction)的后续请求将使用相同的SqlConnection。

还要注意,对于SQL Server,有些事务不能在事务内部执行。 您不能在事务中创建或更改数据库。 但是您可以在事务中的数据库内部创建表和索引。 其他平台有不同的限制。 例如,Oracle自动提交DDL语句。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM