Injection Dependency: Bring your own container in .NET Core console App, example with Simple Injector

Introduction of Simple Injector

Simple Injector is an easy-to-use Dependency Injection (DI) library for .NET that supports .NET Core, Xamarin, Mono and Universal apps. Simple Injector is easily integrated with frameworks such as Web API, MVC, WCF, ASP.NET Core and many others. It’s easy to implement the dependency injection pattern with loosely coupled components using Simple Injector.

Why Simple Injector? It’s simple to use, free, fast, support advanced generics types, and provide powerful diagnostics services.

If you want to know more about you can check the documentation here: https://simpleinjector.readthedocs.io/en/latest/quickstart.html

Installation of Simple Injector in a console app

Select and Install it from “Manage Nuget packages” panel

or type the following command in “Package manager console”:

PM> Install-Package SimpleInjector -Version 4.0.12

Configuration of Simple Injector in a console app

  • Import SimpleInjector and SimpleInjector.Lifestyles namespaces
  • Add a static Container property in your class Program
  • Register your service with its appropriate Interface, Simple Injector supports concrete injection (the class without its interface)
  • Optionnally add Verify method, it iterates registered service to check if something is not correct, will throw an exception before any execution of the progam
  • Then use GetInstance method to get your service

Example :

public interface IMyService
{
   string HelloWorld();
}
public class MyService: IMyService
{
   public string HelloWorld()
   {
      return "Hello world!";
   }
}
using SimpleInjector;
using System;

namespace ConsoleAppDemoWithSimpleInjector
{
   class Program
   {
      static readonly Container container;

      static Program()
      {
         container = new Container();

         container.Register<IMyService, MyService>();

         container.Verify();
      }

      static void Main(string[] args)
      {
         var service = container.GetInstance<IMyService>();
         Console.WriteLine(service.HelloWorld());
         Console.ReadLine();
      }
   }
}

Execution:

Configuration of Simple Injector in a console app thats runs undefinitely

In the absence of any framework code, you are yourself responsible to tell Simple Injector that certain code must run in isolation. This can be done with Scoping. There are two types of scoped lifestyles that can be used. ThreadScopedLifestyle allows wrapping code that runs on a single thread in a scope, where AsyncScopedLifestyle allows wrapping a block of code that flows asynchronously (using async await).

The following example demonstrates a simple Console application that runs indefinitely, and executes a request every second. The request is wrapped in a scope:

class Program
{
   static readonly Container container;

   static Program()
   {
      container = new Container();
      container.Options.DefaultScopedLifestyle = new ThreadScopedLifestyle();

      container.Register<IMyService, MyService>();

      container.Verify();
   }

   static void Main(string[] args)
   {
      while (true)
      {
         using (ThreadScopedLifestyle.BeginScope(container))
         {
            var service = container.GetInstance<IMyService>();

            Console.WriteLine(service.HelloWorld());
         }

         Thread.Sleep(TimeSpan.FromSeconds(1));
      }
   }
}

By default the lifecycle of our service is Transient, it means that a new instance will be created each we ask an instance of our service, else you can set Singleton.

Transient lifestyle

container.Register<IMyService, MyService>(Lifestyle.Transient);

or

container.Register<IMyService, MyService>();

Singleton lifestyle

container.Register<IMyService, MyService>(Lifestyle.Singleton);

Example that display Guid of the instance:

public class MyService: IMyService
{
   private Guid _guid;

   public MyService()
   {
      _guid = Guid.NewGuid();
   }

   public string HelloWorld()
   {
      return $"Hello world! instance: {_guid}";
   }
}

Execution :

Transient lifestyle

Guid are not identicals

Singleton lifestyle

Guid are identicals

 

Simple isn’it ? 🙂

Entity Framework Core 2 – Breaking changes and obsolescence

 

Entity Framework Core 2 was released on August 14th. It brought new features.

On this article I will explain : Breaking changes and obsolete features

The IDbContextFacfory<T> interface was replaced by IDesignTimeDbContextFactory<T>.

This interface is required when you want to add new migration and update database.

Example:

public class AdventureWorksContextScaffoldedFactory : IDesignTimeDbContextFactory<AdventureWorksContext>
{
   public AdventureWorksContext CreateDbContext(string[] args)
   {
      var builder = new DbContextOptionsBuilder<AdventureWorksContext>();
      builder.UseSqlServer(@const.connectionStringGenerated);
      return new AdventureWorksContext(builder.Options);
   }
}

The extension method UseMemoryDatabase has changed

Now it’s strongly recommanded to use the signature with an in memory database name, because you may have issues if you use multiple databases in memory.

Example:

var serviceProvider = new ServiceCollection()
 .AddDbContextPool<AdventureWorksContext>(
    options =>
    {
       options.UseInMemoryDatabase("AdventureWorks");
    })
 .AddScoped<IEfQueries, EfQueries>()
 .BuildServiceProvider();

If you don’t use the signature with a name, you will get an “obsolete” warning:

Other changes

Package Microsoft.EntityFrameworkCore.SqlServer.Design is deprecated in favor of Microsoft.EntityFrameworkCore.Design(now provider-agnostic).

Only 2.0 providers will work, so any existing providers that target EF Core 1.x will need to be rewritten.

Logging event IDs have changed from the previous version and they are now identical to those used by corresponding ILogger messages. The logger categories now come from subclasses of DbLoggerCategory, such as DbLoggerCategory.Database.CommandDbLoggerCategory.MigrationsDbLoggerCategory.Infrastructure, etc, all of which offer a Name property.

Entity Framework Core 2 – DbContext Pools

 

Entity Framework Core 2 was released on August 14th. It brought new features.

On this article I will explain one of them: DbContext Pools

Normally when a DbContext is injected somewhere by the dependency injection framework, a new instance is created every time. With this, we can have a pool of instances, 128 by default. It is a performance improvement and it is configured like this (console app example):

var serviceProvider = new ServiceCollection()
 .AddDbContextPool<AdventureWorksContext>(options => { //options })
 .AddScoped<IEfQueries, EfQueries>()
 .BuildServiceProvider();

.AddDbContext from Entity Framework Core 1 is still implemeted in Entity Framework Core 2.

What kind of improvment to expect ?

Let’s build a Query and let’s see the improvment by using 2 instances of the queries service intantiated by injection dependency system:

public Orders GetOrderById(int id)
{
   return _context.WorkOrders.Select(
                             x => new Orders
                             {
                                Id = x.WorkOrderId,
                                ProductName = x.Product.Name,
                                Quantity = x.OrderQty,
                                Date = x.DueDate
                             }).FirstOrDefault(x => x.Id == id);
}

 

// Using AddDbContext 
var efqueriesService1 = serviceProvider.GetService<IEfQueries>();
efqueriesService1.GetOrders();
var efqueriesService2 = serviceProvider.GetService<IEfQueries>();
efqueriesService2.GetOrders();

// Using AddDbContextPool
var efqueriesService1 = serviceProvider.GetService<IEfQueries>();
efqueriesService1.GetOrders();
var efqueriesService2 = serviceProvider.GetService<IEfQueries>();
efqueriesService2.GetOrders();

I executed this serie of execution 20 times and I measured the execution (using Stopwatch object) this:

With AddDbContext:

  • Instance 1: 23 to 29 ms (instantiation + execution)
  • Instance 2: 7 to 9 ms (instantiation + execution)

With AddDbContextPool:

  • Instance 1: 17 to 21 ms (instantiation + execution)
  • Instance 2: 5 to 7 ms (instantiation + execution)

It looks a bit more performant! 😉