为什么我的 ef 核心数据上下文在用于被测服务之前就已处理?

Why is my ef core data context disposed before use in a service under test?

背景:我正在围绕使用 ef 核心的服务编写测试。我想使用 sqllite,因为它是关系型的。

我已经为测试编写了一个基础 class,它将使用我编写的模拟数据库工厂来设置基本的通用事物,例如 http 模拟和 DAL。

namespace Bll.UnitTests
{
    public class TestBase : IDisposable
    {
        // pass httpclient as dependency, setup messageHandler for stubbing
        protected HttpClient httpClient;
        protected Mock<HttpMessageHandlerFake> fakeHttpMessageHandler = new Mock<HttpMessageHandlerFake> { CallBase = true };

        protected Mock<Logger> loggerMock;
        protected DalContext dataContext;

        protected MockDbFactory mockDbFactory;

        public TestBase()
        {
            mockDbFactory = new MockDbFactory();

            httpClient = new HttpClient(fakeHttpMessageHandler.Object);
            dataContext = mockDbFactory.testDb;
            loggerMock = new Mock<Logger>(dataContext);
        }

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

这是我的模拟数据库工厂,它应该只在内存中设置一个连接并且似乎可以工作。

using Dal;
using Microsoft.EntityFrameworkCore;
using Moq;
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;

namespace Bll.UnitTests.Factories
{
    // In-memory database only exists while the connection is open
    public class MockDbFactory : IDisposable
    {
        private SqliteConnection connection;
        public DalContext testDb;

        public MockDbFactory()
        {
            OpenConnection();
            testDb = GetTestDb();
        }

        public void Dispose()
        {
            CloseConnection();
        }

        private void OpenConnection()
        {
            connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();
        }

        private void CloseConnection()
        {
            connection.Close();
        }

        private DalContext GetTestDb()
        {
            var options = new DbContextOptionsBuilder<DalContext>()
                .UseSqlite(connection)
                .Options;

            // Create the schema in the database
            using (var context = new DalContext(options))
            {
                context.Database.EnsureCreated();
                return context;
            }
        }
    }
}

在我的测试中 class datacontext 在我调试被测服务时被释放。

public class LocationServiceTest : TestBase
    {
        private LocationService sut;

        public LocationServiceTest(): base()
        {
            sut = new LocationService(
                httpClient,
                loggerMock.Object,
                dataContext
            );
        }

        [Fact]
        public async Task UpdateCountriesAsync_CallsCountryApiAndUpdatesDatabase()
        {
            // arrange
            // setup get country data to return 2 countries
            var temp = BuildCountryApiReturnable(2);
            fakeHttpMessageHandler.Setup(f => f.Send(It.IsAny<HttpRequestMessage>())).Returns(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(temp)
            });

            // act
            try
            {
                var result = await sut.UpdateCountriesAsync();

                // assert
                Assert.True(dataContext.Country.Count() == 2);
                Assert.True(dataContext.Location.Count() == 2);
            }
            catch(Exception e)
            {
                throw e;
            }

        }

我想我知道 using 语句是必要的,因为这将创建我的连接并处理它,但我正在尝试手动执行此操作,以便我可以将数据上下文注入我的服务。如果我必须将所有内容都包装在 using 语句中,我将被迫更改我的服务..

回答你的问题: 在您的 MockDbFactory 中,您已经通过 using 子句处理了上下文:

private DalContext GetTestDb()
{
    var options = new DbContextOptionsBuilder<DalContext>()
        .UseSqlite(connection)
        .Options;

    // Create the schema in the database
    using (var context = new DalContext(options))
    {
        context.Database.EnsureCreated();
        return context;  // context will already be disposed after returning
    }
}

您应该启动一个新的 DalContext 实例并在您的 MockDbFactory.Dispose 方法中处理它:

private DalContext GetTestDb()
{
    ...
    testDb = new DalContext(options);
    //Other configurations
}
...
public void Dispose()
{
    CloseConnection();
    testDb.Dispose();
}