Entity framework core and enhanced testability

Entity framework aka EF is a powerful ORM tool. After using it for many years on full Net-framework(until EF6), I came across netCore version of it. If you have started working with netCore stack, this post will help a great deal to start consuming netCore EF.

NetCore is an open source, cross-platform framework which can be used to develop .net applications and run them on almost all known operating systems e.g. Linux, Windows and macOS. To use EF in a netCore application, you have to go with netCore version of it.

In addition to EF6 features, netCore EF has an OOTB(out of the box) inMomory database provider which can be used to create a logical relational database in the memory and use it until the life cycle of the application. This is very powerful when it comes down to writing integration tests. In netCore application, we can register the services on startup of the application. This means, our main application(could MVC web or console app) will be registering a service to use SQL database provider while our test application can register a service to use inMomory provider.

Starting off with the main application(web or console), we need to reference following Nuget Packages.

  1. Microsoft.EntityFrameworkCore
  2. Microsoft.EntityFrameworkCore.SqlServer(this has a dependency on the first one, installing this only should be enough)

These two packages you can get from the standard nuget server https://api.nuget.org/v3/index.json

With these packages in place, you need to create database context.

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

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
        }

        public DbSet Institutes { get; set; }
        public DbSet Institutes { get; set; }
    }

The constructor of this class takes in DBContextOptions which can be used to toggle between SQL and InMemory during the startup of the application.

In netCore applications, we have a Startup class which is called implicitly by the framework to set up the application context. The ConfigureServices method of this class is used to configure the services. Following piece of code, registers dbContext for SQLServer.

        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("MyConnectionString")));            

            var container = new Container();

            container.Configure(config =>
            {
                config.AddRegistry(new DefaultRegistry());
                config.Populate(services);
            });

            return container.GetInstance();
        }

This should get us going with EF in our application. We can inject MyContext in classes where we need it to read/write to the database.

    public class LoginHandler 
    {
        private readonly MyContext _context;

        public LoginHandler(MyContext context)
        {
            _context = context;
        }

        public void Handle()
        {
            _context....
        }
    }

 

If you haven’t already noticed, we mentioned a connection string name while registering the dbContext service

services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("MyConnectionString")));

This connection string needs to be added to appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "ConnectionStrings": {
    "MyConnectionString": "Server=(local);Database=MyDatabase;Trusted_Connection=True;"
  },
  "AllowedHosts": "*"
}

 

While writing your integration tests application, we will just need to tell MyContext class to use inMomory provider. This will erase the limitation of having a database server for integration tests. We can configure this option in the Startup/ConfigureServices method of  our test application.

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext(options => options.UseInMemoryDatabase("MyDB"));            

            var container = new Container();

            container.Configure(config =>
            {
                config.AddRegistry(new DefaultRegistry());
                config.Populate(services);
            });

            return container.GetInstance();
        }

 

Leave a Reply