【问题标题】:one transaction for multiple contexts in integration tests on TestInitialize在 TestInitialize 上的集成测试中针对多个上下文的一项事务
【发布时间】:2018-01-21 19:52:09
【问题描述】:

我正在编写集成测试,我想使用事务范围。 我们使用 EF 和带有上下文的存储库。

如果我有一个存储库和一个上下文,那么它看起来像这样:

[TestInitialize]
public void RuleEngineTestsStart() {
    customContext = new CustomContext();
    transaction = customContext.Database.BeginTransaction();
    repo = new CustomRepository(customContext);

    // I need to make this context to work in the same transaction as above
    anotherContext = new AnotherContext();
    anotherRepo = new AnotherRepository(anotherContext);
}

在测试结束时 (TestCleanup) 我想transaction.Rollback(); 一切。

我希望所有在不同上下文中工作的存储库都有相同的事务,这可能吗? 如何创建事务并将其“发送”到所有三个上下文? 请不要为所有存储库使用一个 Context,由于某些原因,这是不可能的(我们希望每个上下文都有自己的 DbSet,以便稍后在微服务中使用)。


编辑 在 cmets 中,我被要求包含更多代码,但是,我认为没有必要回答我的问题。

customContext = new CustomContext();
repo = new CustomRepository(customContext);

customContext2 = new CustomContext2();
otherRepository = new CustomRepository2(customContext2);

// class to be tested needs both repositories
ToBeTestedClass cl = new ToBeTestedClass(customRepository, otherRepository);

// "BASE" interface
public interface IRepository<TEntity> where TEntity : class
{
    TEntity GetById(long id);
    IEnumerable<TEntity> GetByFilter(Expression<Func<TEntity, bool>> predicate);
    TEntity GetSingleByFilter(Expression<Func<TEntity, bool>> filter);
    void Insert(TEntity entity);
    void Delete(long id);
    void Update(TEntity entity);
    ...
}

 // BASE CLASS
 public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected readonly DbContext _context;
    protected readonly DbSet<TEntity> _dbSet;

    public Repository(ColldeskDbContext context)
    {
        _context = context;
        _dbSet = context.Set<TEntity>();
    }

    // GetSingle, GetAll, Insert, Update etc.

  }

  // CustomRepository (other Repositories are similar, with custom methods)
  public interface ICustomRepository : IRepository<CusotmData>
   {
         // some specific methods that are not in Base class   
   }

  public class CustomRepository: Repository<CustomData>, ICustomRepository
  {
    public CustomRepository(CustomContext context) : base(context)
    {
    }
    // custom methods that are specific for given context
  }

  // Contexts - each context consists of its one DbSets

【问题讨论】:

  • 以伪代码显示您的存储库接口和其中一个的构造函数,以及您的场景中涉及的 2-3 个存储库。强烈建议使用单独的数据库进行测试。使用生产数据库进行测试不是一个好主意。
  • 对于多个上下文/存储库:我们有需要测试的代码并使用两个不同的存储库。第二个是这样的: var customContext2 = new CustomContext2();和 repo2 = new CustomRepository2(customContext2);
  • 我添加了更多代码。

标签: entity-framework transactions integration-testing


【解决方案1】:

不要在您的存储库中使用dbContext.SaveChanges()。创建存储库时使用 ONE dbContext。示例:

        using ( var db = new YourDbContext() )
        {
            // Create and begin transaction
            using ( var transaction = db.Database.BeginTransaction() )
            {
                try
                {
                    // ONE dbContext for all repositories
                    var firstRepo = new Custom1Repository(db);
                    var secondRepo = new Custom2Repository(db);

                    City city = new City { Description = "My city" };
                    Street street = new Street { Description = "My street", City = city};
                    firstRepo.Insert(city);
                    secondRepo.Insert(street);

                    // Save all your changes and after that commit transaction
                    db.SaveChanges();
                    transaction.Commit();
                }
                catch ( Exception ec)
                {
                    transaction.Rollback();
                }
            }
        }

这样做,您的存储库将成为 DbSet&lt;TEntity&gt; 的包装器

【讨论】:

  • 我们已经有多个上下文。这无法更改,因为每个上下文都有自己的数据库集。我们没有所有数据库集的一个上下文。有原因......顺便说一句,我们在上下文中没有Savechanges。因此你的答案不适合我。
  • 不同的上下文代表不同的模式或数据库?
【解决方案2】:

我发现我可以像这样简单地使用 TransactionScope:

         private TransactionScope _scope;

        [TestInitialize]
        public void TestInitialize()
        {
            _scope = new TransactionScope();
        }

        [TestCleanup]
        public void TestCleanup()
        {
            _scope.Dispose();
        }

然后每个 Context 都会在这个 TransactionScope 中运行。

【讨论】:

    猜你喜欢
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多