首页 > 解决方案 > 没有这样的表 - 内存中带有 Sqlite 的 EF Core

问题描述

我正在尝试设置我的测试环境,但我在使用 Sqlite 适配器时遇到了问题。每个创建的上下文都有相同的连接,因此应该为每个上下文正确构建内存数据库。

但是当我尝试添加新内容时,它会抛出错误:“没有这样的表:%here_is_my_tablename%”。

我觉得我的配置应该不错。

根据:

public abstract class BaseServiceTests : IDisposable
{
    protected readonly SqliteConnection Connection;

    public BaseServiceTests()
    {
        Connection = new SqliteConnection("DataSource=:memory:");
        Connection.Open();
        Assert.NotNull(Connection);

        using (var ctx = BuildCoreContext())
        {
            ctx.Database.EnsureCreated();
        }   

        using (var ctx = BuildWebContext())
        {
            ctx.Database.EnsureCreated();
        }     
    }

    public void Dispose()
    {
        Connection.Close();
    }

    public DbContextOptions<TContext> Options<TContext>() where TContext: DbContext
    {
        var options = new DbContextOptionsBuilder<TContext>()
            .UseSqlite(Connection)
            .Options;

        return options;
    }

    public ServiceRequestCoreContext BuildCoreContext()
    {
        var ctx = new ServiceRequestCoreContext(Options<ServiceRequestCoreContext>(), null);
        ctx.Database.OpenConnection();

        return ctx;
    }

    public ServiceRequestWebContext BuildWebContext()
    {
        var ctx = new ServiceRequestWebContext(Options<ServiceRequestWebContext>(), null);
        ctx.Database.OpenConnection();

        return ctx;
    }
}

测试

public class RequestServiceTests : BaseServiceTests
{
    public async Task Prepare()
    {
        using (var ctx = BuildCoreContext())
        {
            await ctx.RequestTypes.AddAsync(new RequestType((int)RequestTypes.Order, "TestType - test"));
            await ctx.RequestStatuses.AddAsync(new RequestStatus((int)RequestStatuses.AcceptedForVeryfication, "test - test", "test - test"));
            await ctx.Companies.AddAsync(new CustomerCompany(1, "test - test", "Test - test"));
            await ctx.SaveChangesAsync();
        }
    }

    [Fact]
    public async Task when_creating_new_request_it_should_not_be_null()
    {
        //Arrange 
        await Prepare();
        var customerId = 1;
        var iGen = new IdentifyGenerator();

        //Act
        using (var webCtx = BuildWebContext())
        {
            webCtx.Database.OpenConnection();
            var service = new RequestService(webCtx, BuildCoreContext(), iGen);
            await service.CreateAsync(customerId);
            await webCtx.SaveChangesAsync();
        }

        //Assert
        using (var ctx = BuildWebContext())
        {
            ctx.ServiceRequests.Should().HaveCount(1);
            ctx.ServiceRequests.FirstOrDefault().Should().NotBeNull();                
        }
    }
}

标签: c#entity-framework-core

解决方案


要回答 OP,我认为问题在于访问数据库的多个上下文,如果没有共享缓存,这将无法工作。将您的连接字符串更改为:"DataSource=file::memory:?cache=shared"

对于那些从谷歌徘徊在这里的人,这里还有一些其他的事情要记住:

  • 至少一个连接必须保持对数据库的开放。
  • 如果将从多个连接访问数据库,则必须使用共享缓存,如下所示:string connectionString = "DataSource=file::memory:?cache=shared";
  • 如果使用 ADO.NET,官方提供程序无法与实体框架一起正常工作。即使我已经正确配置了所有内容,我在测试时也遇到了“没有这样的表”错误,但是一旦我从:System.Data.SQLite.SQLiteConnection(System.Data.SQLite.Core nuget 包)更改为 Microsoft 提供程序:Microsoft.Data.Sqlite.SqliteConnection然后错误就消失了。
  • 官方的 Sqlite ADO.NET 适配器比微软的快2 倍!因此,除非您绝对必须,否则最好使用官方的 Sqlite 适配器。

示例代码:

[Fact]
public async Task MyTest()
{
    var dbContext = new MyDbContext("DataSource=file:memdb1?mode=memory&cache=shared");

    try
    {
        await dbContext.Database.OpenConnectionAsync();
        // Test your dbContext methods, which has an open connection to the in-memory database
        // If using ADO.NET to make new connections, use Microsoft.Data.Sqlite.SqliteConnection
    }
    finally
    {
        await dbContext.Database.CloseConnectionAsync();
    }
}

还记得阅读文档!:)

更新:为了使内存数据库的测试更容易,我创建了一个继承自我的 DbContext 的 TestContext,以便自动处理连接和数据库创建:(这次使用 NUnit)

public sealed class TestContext : UsersContext
{
    public TestContext(string connectionString) : base(connectionString)
    {
        Database.OpenConnection();
        Database.EnsureCreated();
    }

    public override void Dispose()
    {
        Database.CloseConnection();
        base.Dispose();
    }
}

用法:

[Test]
public void AddingUsersShouldIncrementId()
{
    using (var dbContext = new TestContext("DataSource=file::memory:?cache=shared"))
    {
        dbContext.UsersDbSet.Add(new UserEntity());
        dbContext.SaveChanges();

        uint actualId = dbContext.Users.Single().Id;
        Assert.AreEqual(1, actualId);
    }
}

推荐阅读