7

I need to replace context in WebApplicationFactory. I have MyDbContext which I want to replace with SQLite context for testing.

The replace part works fine

.ConfigureServices(services =>
  {
    // Remove the app's ApplicationDbContext registration.
    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<MyDbContext>));

    if (descriptor != null)
    {
      services.Remove(descriptor);
    }
    
    services.AddDbContext<MyDbContext>(builder =>
    {
      builder.UseSqlite(CreateInMemoryDatabase());
    });
  });

But because I moved from Npgsql to SQLite in tests I need to override some default values in OnModelCreating. I created a new db context class

public class MySqlLiteDbContext: MyDbContext
    {
        public MySqlLiteDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Record>()
                .Property(b => b.DateCreated)
                .HasDefaultValueSql("(datetime('now'))");

            ...
        }
    }

Is there any way to inject MySqlLiteDbContext instead of MyDbContext to force EnsureCreated use OnModelCreating for SQLite? Is extracting IMyDbContext an option? What else can I do to solve this problem?

3 Answers 3

4

Ok, looks like I managed to solve it like this

  1. Add non-generic DBContext constructor override, make it protected
public class MyDbContext: DbContext {        
    public MyDbContext(DbContextOptions<MyDbContext> options): base(options) {}
    // new
    protected MyDbContext(DbContextOptions options): base(options) {}
        
    // rest context stuff...
}
  1. Remove DBContext and DBContextOptions in WebApplicationFactory on ConfigureServices
.ConfigureServices(services => {
    // Remove the app's ApplicationDbContext registration.
    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof (DbContextOptions<MyDbContext>));

    if (descriptor != null) {
        services.Remove(descriptor);
    }

    var descriptor2 = services.SingleOrDefault(d => d.ServiceType == typeof (MyDbContext));

    if (descriptor2 != null) {
        services.Remove(descriptor2);
    }

    //...
})
  1. Add your descendant context as implementation
.ConfigureServices(services => {
    // Remove the app's ApplicationDbContext registration.

    // ...
    services.AddDbContext<MyDbContext, MySqliteDbContext>(builder => {
        builder.UseSqlite(CreateInMemoryDatabase());
    }, ServiceLifetime.Singleton);
})

Note: If you use InMemory Sqlite consider Singleton scope.

  1. Done. Now when DocumentComparisonContext injected DocumentComparisonSqlLiteContext will be used as implementation, and on EnsureCreated sqlite-specific logic will be used.
Sign up to request clarification or add additional context in comments.

Comments

2

Register DbContext with the Services Container. Open Startup.cs and in the ConfigureServices function, we are going to use the AddDbContext extension method to add our new DbContext and tell it to use SQLite with the connection string from our appsettings.json. The following is the full function with the first two lines being the ones we added.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ContactsDbContext>(options =>
        options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
    services.AddControllers();
    services.AddOpenApiDocument(document => 
        document.PostProcess = d => d.Info.Title = "Contacts API");
}

Comments

1

Thanks for guidance. There is no need to remove the context, it suffice with the options class.

var contextOptions = new DbContextOptionsBuilder<MyDbContext>()
            .UseInMemoryDatabase("<name>");
services.AddScoped<DbContextOptions<MyDbContext>>(_ => contextOptions.Options);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.