Совместное использование транзакции между EF DbContext в отдельных проектах - PullRequest
0 голосов
/ 15 декабря 2018

Я хочу написать несколько интеграционных тестов для микросервисной среды, используя 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();
    }
}

Они будут пройдены, и все хорошо, КРОМЕ ТОГО, У меня нет доступа к микро сервисамчтобы установить их транзакцию или, по крайней мере, я понятия не имею, возможно ли это вообще или нет.Я имею в виду, что было бы очень здорово, если бы я мог сконфигурировать или дать сигнал микросервису использовать транзакцию для своей БД вне области действия этого приложения.

Кроме того, если это невозможно, что вы можете сделатьПредлагаю использовать в этой ситуации я хочу получить изоляцию для каждого тестового сценария и при этом иметь возможность запускать их параллельно.

1 Ответ

0 голосов
/ 15 декабря 2018

Для большинства провайдеров это «просто работает».TransactionScope устанавливает статическое свойство Transaction.Current , и поставщики ADO.Net могут (и обычно делают) подключаться к внешней транзакции.См .: Реализация неявной транзакции с использованием объема транзакции

И, по крайней мере, для SQL Server, если ваши тесты используют одну и ту же строку подключения и не работают одновременно, они также будут использовать тот же SqlConnection и не требовать продвижения в Распределенную транзакцию.Поскольку пул соединений SQLClient разделен по транзакциям, и последующий запрос к пулу для него (ConnectionString, транзакция) будет использовать тот же SqlConnection.

Также обратите внимание, что для SQL Server существуют некоторые операции инициализации, которые могутне может быть выполнено внутри транзакции.Вы не можете создать или изменить базу данных в транзакции.Но вы можете создавать таблицы и индексы внутри базы данных в транзакции.Другие платформы имеют другие ограничения.Например, Oracle автоматически фиксирует операторы DDL.

...