在内存中使用SQLite运行EF Core - 没有配置数据库提供者

问题描述 投票:2回答:1

我正在使用EF Core与SQL server和StructureMap为IOC编写一个ASP.Net Core Web API。这些库项目都是 .NetStandard2.0 而API和单元测试则是 NetCoreApp2.0.

对于单元测试,我正在运行XUnit,并将SQL Server换成内存中的SQLite数据库,因为它提供了完整的参考完整性。我仍然使用与我的主代码相同的IOC设置,但我传递了一个Structure map注册表的列表,以允许我在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 属性,以确保一旦上下文被处理,我不会丢失db。

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 具备以下条件。

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

我试过了所有能找到的解决方法 I have tried every solution I can find to this problem. 添加了 .AddDbContext 到服务集合。确保测试项目和上下文项目都有引用到 EntityFrameworkCore, EntityFrameworkCore.RelationalEntityFrameworkCore.Sqlite. 确保SQLite连接保持活跃,并确保测试设置使用的是 .AddEntityFrameworkSqlite() 在...上 ServiceCollection.

我也试过把SQLite换成EF Cores。InMemory Db与另一个上下文工厂,但失败与完全相同的问题。

有谁见过这个问题,或者我在使用EF Core时,是否以某种方式使它与SQLite不兼容?

c# entity-framework sqlite entity-framework-core asp.net-core-2.0
1个回答
0
投票

我在前段时间研究过这个问题。而且我相信你需要运行迁移并保持连接开放。你正在覆盖连接的静态。另外,我不确定它在你使用连接之前是否会被创建。

© www.soinside.com 2019 - 2024. All rights reserved.