InMemoryDatabase 有连接字符串吗?

Bar*_*cer 8 c# testing xunit entity-framework-core

因此,我们在使用 MediatR 的应用程序中使用 CQRS 模式。我正在开发一个基本的 CRUD API,具有以下规定:

  • GET 调用需要使用 QueryHandlers、Dapper、SQL 从数据库检索数据。
  • PUT/POST/DELETE 调用需要使用 CommandHandlers/Entity Framework 来修改数据库。

此时我已使其按预期工作。然而,我发现测试是一场噩梦,因为我无法找出访问内存数据库所需的连接字符串。

我尝试过connectionString = "Data Source=:memory:;Mode=Memory;Cache=Shared";这些价值观的各种排列,但没有一个有效。

我看过 SQLite,但它不喜欢我尝试在启动时注册两个不同的数据库(SQLite 用于读取,Microsoft.EntityFrameworkCore.InMemory 用于写入),我试图获取的对象也有主键和bigint/longSQLite似乎只能处理integer/int类型主键,因为它会抛出“只有整数类型的主键可以具有自动增量”。

我正在使用类似于以下的代码设置 XUnit 测试:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
   protected override void ConfigureWebHost(IWebHostBuilder builder)
   {
        builder.ConfigureServices(svcs => 
        {
           services.AddDbContextPool<MyDbContext>(opts =>
           {
              options.UseInMemoryDatabase("MyTestDB");
           });
         
           using var scope = svcs.BuildServiceProvider().CreateScope();
           var scopedSvcs = scope.ServiceProvider;
           var dbSvc = scopedSvcs.GetRequiredService<MyDbContext>();

           dbSvc.Database.EnsureCreated();


           // Do DB Seeding stuff
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

在我使用的查询处理程序中,SqlConnection如下所示:

public class GetModelHandler : IRequestHandler<GetModelRequest, MyModel>
{
   private readonly string _connString;


   public GetModelHandler(string connString) { _connString = connString; }


   public async Task<MyModel> Handle(GetModelRequest req, CancellationToken token)
   {
       using (var conn = new SqlConnection(_connectionString)) 
       {
          conn.Open();
          // set command info and make call
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

所以,TL;DR 的问题是……“是否有一个连接字符串可用于访问 Entity Framework Core 内存数据库,如果有,我如何找到它?”

Mar*_*sta 2

当您在内存中运行数据库时,一旦连接关闭,其中的数据就会丢失。如果你想保留数据,你可以使用它创建一个数据库文件

startup.cs在文件中

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{

  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
    app.UseMigrationsEndPoint(); //package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

...

    var connection = new SqliteConnectionStringBuilder(Configuration.GetConnectionString("yourConnection"));
    try
    {

      // Keep this line if you want to recreate the database every time you run your app
      if (File.Exists(connection.DataSource)) File.Delete(connection.DataSource);


      var connectionFolder = Directory.GetParent(connection.DataSource);
      if (!Directory.Exists(connectionFolder.FullName)) Directory.CreateDirectory(connectionFolder.FullName); // Create folder

      File.WriteAllBytes(connection.DataSource, new byte[0]); //Create database

    }
    catch
    {

    }
...
  }
  
var context = app.ApplicationServices.GetRequiredService<IdentityContext>();
context.Database.EnsureCreated();

...
}
Run Code Online (Sandbox Code Playgroud)

对于上述代码的工作,您需要从依赖项注入器容器中停用范围验证。您可以在program.cs

program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
            .UseDefaultServiceProvider(options => options.ValidateScopes = false); // Add this line.

Run Code Online (Sandbox Code Playgroud)

appsettings.json

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
            .UseDefaultServiceProvider(options => options.ValidateScopes = false); // Add this line.

Run Code Online (Sandbox Code Playgroud)