Why do I need to override the constructor for the parent class DbContext when the sub class doesn't add any extra logic to the constructor?

Viewed 49

I've created a subclass of Microsoft.EntityFrameworkCore.DbContext (called AppContext) and I am creating an instance of AppContext in my test class.

I've added an empty constructor to AppContext to resolve a build error: CS1729 'AppContext' does not contain a constructor that takes 1 arguments. I can't figure out why I need to do this.

The parent class DbContext appears to have a constructor that takes a single argument of type Microsoft.EntityFrameworkCore.DbContextOptions. When I call the constructor for AppContext I'm passing an argument of type DbContextOptions<AppContext>.

AppContext:

using Microsoft.EntityFrameworkCore;
using OMSBackend.Models;

namespace OMSBackend
{
    public class AppContext : DbContext
    {
        // A build error occurs if I comment out this constructor.
        public AppContext(DbContextOptions options) : base(options)
        {
        }

        public DbSet<User> Users { get; set; }
    }
}

InMemoryDatabaseTestBase:

using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;

namespace OMSBackend.Tests.Unit
{

    public abstract class InMemoryDatabaseTestBase : IDisposable
    {
        private DbContextOptions<AppContext> _contextOptions;
        private SqliteConnection _connection;

        protected InMemoryDatabaseTestBase()
        {
            _connection = OpenInMemorySqliteConnection();
            _contextOptions = new DbContextOptionsBuilder<AppContext>()
                .UseSqlite(_connection)
                .Options;
        }

        public void Dispose()
        {
            _connection.Dispose();
        }

        protected AppContext CreateContext()
        {
            return new(_contextOptions);
        }

        private SqliteConnection OpenInMemorySqliteConnection() {
            _connection = new SqliteConnection("Filename=:memory:");
            _connection.Open();
            return _connection;
        }
    }
}

(The using statements are present because I've disabled ImplicitUsings in my project.)

I'm new to C#, but it seems like I shouldn't need to override the constructor here. Is there something I've overlooked that would allow me to extend DbContext without overriding the constructor?

I've used these articles as a guide:

https://docs.microsoft.com/en-us/ef/core/testing/testing-without-the-database#sqlite-in-memory

https://www.meziantou.net/testing-ef-core-in-memory-using-sqlite.htm (Gérald's SampleDbContext class also overrides the constructor, as I've had to do.)

2 Answers

A class doesn't automatically inherit a constructor from its base class. All constructors for a class, other than the default (parameterless) constructor, must be implemented in that class. You aren't actually overriding anything in that code. You are implementing a new constructor in your derived class and that invokes a constructor in the base class. A derived constructor will often invoke a base constructor with the same parameters but it doesn't have to do so.

If you haven't called a constructor, then the behaviour of accessing a member of a type wouldn't be well defined. So as a general rule that applies to all Object Oriented programming languages, when constructing a class you must first construct a valid instance of your base class.

The C# compiler will help you handle the common case of parameter-less constructors in a couple of ways.

  • if the base class has a parameter-less constructor, it will be called implicitly.
  • if you haven't provided any explicit constructors, the compiler will generate a parameter-less constructor for you.
Related