簡體   English   中英

在內存中使用 SQLite 運行 EF Core - 未配置數據庫提供程序

[英]Running EF Core with SQLite in memory - No Database Provider has been configured

我正在使用帶有 SQL 服務器的 EF Core 和用於 IOC 的 StructureMap 編寫 ASP.Net Core Web API。 庫項目都是.NetStandard2.0而 API 和單元測試是NetCoreApp2.0

對於單元測試,我正在運行 XUnit 並將 SQL Server 換成內存中的 SQLite 數據庫,因為它提供了完整的參照完整性。 我仍然使用與我的主代碼相同的 IOC 設置,但我傳入了一個結構映射注冊表列表,以允許我替換 SQLite 上下文工廠而不是 sql 工廠。

這是我的 DbContext 的精簡版:

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions options) : base(options)
    {
    }
}

這是我的測試設置代碼:

public IServiceProvider ServiceProvider { get; set; }

public MyServiceTests()
{
    var services = new ServiceCollection();
    var dataRegistry = new Registry();

    dataRegistry.For<IContextFactory<MyDbContext>>().Use<SqlLiteContextFactory>();

    if (SqlLiteContextFactory.Connection == null)
    {
        SqlLiteContextFactory.Connection = new SqliteConnection("DataSource=:memory:");
        SqlLiteContextFactory.Connection.Open();
    }

    services
        .AddEntityFrameworkSqlite()
        //This is only here as some posts suggested this was needed, StartUp.cs for production site does not have this and works fine.
        .AddDbContext<MyDbContext>(options => options.UseSqlite(SqlLiteContextFactory.Connection));

    var registries = new List<Registry>
    {
        dataRegistry,
        new CommandQueryRegistry(),
        new ServiceRegistry(),
        new TransformRegistry()
    };

    ServiceProvider = DependencyInjection.TestSetup(services, registries);
}

IOC 初始化代碼相當基本:

public static IServiceProvider TestSetup(IServiceCollection services, List<Registry> registries)
{
    var container = new Container();

    var registry = new Registry();
    registries.ForEach(r => registry.IncludeRegistry(r));

    container.Configure(config =>
    {
        config.AddRegistry(registry);
        config.ForSingletonOf<IHttpContextAccessor>().Use<HttpContextAccessor>();
        config.Populate(services);
    });

    var serviceProvider = container.GetInstance<IServiceProvider>();
    return serviceProvider;
}

這是我的 SQLite 上下文工廠的上下文工廠代碼,它和 SQL 之間的唯一區別是我有靜態Connection屬性,以確保在處理上下文后我不會丟失數據庫。

public class SqlLiteContextFactory : IContextFactory<MyDbContext>
{
    public static SqliteConnection Connection;

    private DbContextOptions<MyDbContext> CreateOptions(bool trackChanges)
    {
        var builder = new DbContextOptionsBuilder<MyDbContext>();
        var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
        optionsBuilder.UseSqlite(Connection);

        if (!trackChanges)
        {
            builder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
        }

        return builder.Options;
    }

    private MyDbContext CreateDbContext(bool trackChanges)
    {
        if (Connection == null)
        {
            Connection = new SqliteConnection("DataSource=:memory:");
            Connection.Open();
        }            

        var context = new MyDbContext(CreateOptions(trackChanges));

        //Always keep context as most recent version in tests
        context.Database.Migrate();

        return context;
    }

    public MyDbContext CreateNonTrackedContext()
    {
        return CreateDbContext(false);
    }

    public MyDbContext CreateDbContext()
    {
        return CreateDbContext(true);
    }
}

問題

我的代碼在運行站點時運行良好,SQL 上下文工廠創建上下文並運行 migrate 命令來創建數據庫沒有問題。 但是,當我嘗試通過單元測試測試我的任何服務時,上下文工廠在嘗試使用以下內容運行Migrate時會Migrate

System.InvalidOperationException : No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetRelationalService[TService](IInfrastructure`1 databaseFacade)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
   at API.Test.Utilities.SqlLiteContextFactory.CreateDbContext(Boolean trackChanges) in API.Test\Utilities\SqLiteContextFactory.cs:line 37
   at API.Test.Utilities.SqlLiteContextFactory.CreateDbContext() in API.Test\Utilities\SqLiteContextFactory.cs:line 49
   at API.CommandQueries.Commands.MyCommand.AddOrUpdate(MyModel model) in \API.CommandQueries\Commands\MyCommand.cs:line 21
   at API.Services.MyService.Save(Model model) in API.Services\MyService.cs:line 40
   at API.Test.MyTests.CanAdd() in API.Test\MyServiceTests.cs:line 47

我已經嘗試了我能找到的解決這個問題的所有解決方案。 .AddDbContext添加到服務集合中。 確保測試項目和上下文項目都引用了EntityFrameworkCoreEntityFrameworkCore.RelationalEntityFrameworkCore.Sqlite 確保 SQLite 連接保持活動狀態並確保測試設置在ServiceCollection上使用.AddEntityFrameworkSqlite()

我還嘗試使用另一個上下文工廠將 SQLite 換成 EF Cores InMemory Db,但由於完全相同的問題而失敗。

之前有沒有其他人看到過這個問題,或者我是否以某種方式使用 EF Core 使其與 SQLite 不兼容?

不久前我在這方面工作過。 而且我相信您需要運行遷移並保持連接打開。 您正在覆蓋靜態連接。 另外,在您使用連接之前,我不確定它是否已創建。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM