c# - EntityFramework、DbContextScope 和 Effort - 异常 : DbContext has been disposed inside unit test

标签 c# entity-framework unit-testing dbcontext effort

我正在尝试为使用的服务层编写单元测试(使用 NUnit):

  1. 作为数据访问层的 Entity Framework
  2. DbContextScope用于管理 DbContext 生命周期

我也用Effort.EF6用于在单元测试中模拟 DbContext。不幸的是,我找不到使 DbContextScope 与 Effort 兼容的方法,以便我可以正确测试所有情况。


代码概览

服务层由执行某些业务逻辑的类(服务)组成。每个方法都被视为一个完整的事务,以 context.SaveChanges() 结束。示例:

    private IDbContextScopeFactory _dbContextScopeFactory;

    public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory)
    {
        _dbContextScopeFactory = dbContextScopeFactory;
    }

    public BusinessModel.Department Insert(BusinessModel.Department department)
    {
        using (var dbContextScope = _dbContextScopeFactory.Create())
        {
            // Validation
            ValidateAndThrowOnFailure(department, new DepartmentAddValidator());

            // Operation
            DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department);

            newDepartment.InsertDateUTC = DateTime.UtcNow;

            dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment);
            dbContextScope.SaveChanges();

            return Mapper.Map<BusinessModel.Department>(newDepartment);
        }
    }

为了对这种方法进行单元测试,我在每次测试前都做了一些准备工作:

    private IDepartmentsService _departmentsService;
    private IDbContextScopeFactory _dbContextScopeFactory;
    private IDbContextFactory _dbContextFactory;
    private DBModel.DPSContext _dbEntities;

    [SetUp]
    public void ReInitializeTest()
    {
        // Setup DbContext with Effort.EF6
        string connStr = ConfigurationManager.ConnectionStrings["DPSContext"].ConnectionString;
        DbConnection connection = EntityConnectionFactory.CreateTransient(connStr);
        _dbEntities = new DBModel.DPSContext(connection);

        // Fill DbContext with in-memory data
        _dbEntities.Departments.AddRange(DataInitializer.GetDepartments());
        _dbEntities.SaveChanges();

        // Mock IDbContextFactory so that it returns in-memory context
        var contextFactoryMock = new Mock<IDbContextFactory>();

        contextFactoryMock
            .Setup(f => f.CreateDbContext<DBModel.DPSContext>())
            .Returns(_dbEntities);

        _dbContextFactory = contextFactoryMock.Object;

        // Setup DbContextScopeFactory to use mocked context
        _dbContextScopeFactory = new DbContextScopeFactory(_dbContextFactory);
        _departmentsService = new DepartmentsService(_dbContextScopeFactory);
    }

测试和问题

这是一个简单的单元测试:

    [Test]
    public void Insert_WhenValidModelPassed_ShouldInsertNewRecord()
    {
        // Given
        BusinessModel.Department newDepartment = DataInitializer.GetExampleOfNewDepartment();

        // When
        _departmentsService.Insert(newDepartment);

        // Then
        Assert.AreEqual(3, _dbEntities.Departments.Count());
    }

问题是测试失败并出现异常:

System.InvalidOperationException : The operation cannot be completed because the DbContext has been disposed.

似乎在 Insert 方法内部使用的 DbContextScope 在 using block 的末尾内部处理分配的上下文,因此 Assert 在以下情况下抛出异常调用。有没有人遇到过类似的问题,或者只是知道我应该怎么做才能成功测试这个和类似的场景?

最佳答案

对于遇到类似问题的任何人,我创建了一个有点脏但有效的解决方案(至少我希望如此)。除了我在问题中写的内容之外,我还创建了一个派生自真实上下文的类,并使 Dispose 方法不执行任何操作。我还添加了一个在每次测试结束时调用的 RealDispose 方法。

    public class TestableDPSContext : DBModel.DPSContext
    {
        public TestableDPSContext(DbConnection connection)
            : base(connection)
        {

        }

        protected override void Dispose(bool disposing)
        {
            // Do nothing
        }

        public void RealDispose(bool disposing)
        {
            // Invoke real dispose
            base.Dispose(disposing);
        }
    }

    [TearDown]
    public void FinishTest()
    {
        _dbEntities.RealDispose(false);
    }

也许有更好的解决方案,但就目前而言,它似乎解决了 DbContext 在测试中被过早处置的问题。

关于c# - EntityFramework、DbContextScope 和 Effort - 异常 : DbContext has been disposed inside unit test,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43482419/

相关文章:

unit-testing - 何时使用调试与单元测试?

c# - 使用 Mailkit 访问 Exchange 共享文件夹

c# - 如何在 Unity 中创建一个可以显示由许多小图像组成的纹理的着色器

c# - 追踪仅在一个客户的盒子上可见的内存泄漏 (C#) 的最佳方法

c# - 返回任务的方法的不同实现

c# - 将数据库密码注入(inject)默认 EF 连接字符串的最佳位置是什么

c# - 在生产源代码中将 Entity Framework 日志记录设置为调试窗口是否可以?

ios - 单元测试 - 来自 viewcontroller 的调用导航为零

asp.net-mvc - EntityFramework 代码首次迁移在部署到 Azure 后未运行

javascript - 主干测试 - 在 "change"元素上触发 'select' 事件