Introduction
This post is the continuation of the Entity Framework Core Internals series, if you haven’t followed along please check the previous posts below:
- Entity Framework Core Internals: High-Level Overview
- Entity Framework Core Internals: DbContext Instantiation and Initialization
When you work with Entity Framework (EF) Core in your main application, you can decide to work with Dependency Injection (DI) or not DI. Internally, EF Core has its own built-in DI to avoid interfering with application DI, it’s built with Service Oriented in mind, everything is about service, these services can be provided by Core EF or a specific database provider.

Core Services
Core Services are common or default services used by EF Core, but they can be replaced easily, Let’s take a look at the EntityFrameworkServicesBuilder class to see how services are stored and registered.
The first thing you will notice is the public static readonly IDictionary<Type, ServiceCharacteristics> CoreServices property where the characteristics of each service type are defined.

I will not talk about the ServiceLifeTime as we both are familiar with it when working with DI, the important thing we should focus on is the multipleRegistrations parameter. As the name implies if it’s false or not specified, there must be only one registration per type.
Continue exploring the EntityFrameworkServicesBuilder class, the next destination we should stay focused on is the public virtual EntityFrameworkServicesBuilder TryAddCoreServices() method.

You can also notice that some of the services that are used for DbContext Instantiation and Initialization are registered here.
Next, take a look at one of the TryAdd methods to see what it does:

It checks the characteristics of the Service Type and registers the service with the ServiceColectionMap differently based on the MultipleRegistrations flag (whether allowing multiple implementations per service type or not).
Let’s take a look to see how the method TryAdd is implemented in the ServiceCollectionMap class.

As you can see, it ignores if the same service type is already registered once, this is the way other Database Providers use to replace some of the default services provided by EF Core by registering their implementations first and calling TryAddCoreServices() later.
Database Specific Services
Now, let’s go back to the EntityFrameworkServicesBuilder class again and check where the TryAddCoreServices() method gets called.

Not surprisingly, it is overridden and called by other Database Providers to supplement more services or replace default services.
For example: when InMemoryDatabase is registered:

And when SqlServer is registered:

And is overridden and called by EntityFrameworkRelationalServicesBuilder class when SqlServer or Sqlite is registered.


How Service Provider Created
In my previous post: Entity Framework Core Internals: DbContext Instantiation and Initialization, when DbContext is initialized, after calling the OnConfiguring method, it builds fully the Internal Service Provider with the flag providerRequired: true. Let’s recall and examine how the Internal Service Provider is built next.

Let’s navigate to the GetOrAdd method of the ServiceProviderCache class.

Pay attention to the BuildServiceProvider and ApplyServices methods.

Let’s see what the ApplyServices does:

It invokes the ApplyServices for each extension in the options we passed in.
The method is defined in the IDbContextOptionsExtension interface and is implemented by various database providers.


In our BloggingContext we are using throughout the demo of the series, the AddEntityFrameworkSqlite will be called and all the services will be registered:


Back to the DbContext class, as mentioned in the previous post, after the Internal Service Provider is created and cached, it creates a scope and resolves the IDbContextServices, so basically each DbContext instance will have its own scope.

Now that the Internal Service Provider has been created fully and the DbContext is ready to use, before going to the Summary section, let’s back to the GetOrAdd method of the ServiceProviderCache class.
You should notice that the options variable being passed is used as a cacheKey, so for the same DbContext type if there are different options after calling OnConfiguring method, it needs to build and cache different Service Providers.

Summary
So far, we’ve learned how DbContext is instantiated and initialized, what internal service provider is, how it is created, and how different database provider is registered. Before knowing how our queries get processed by EF Core, we need to learn how the Model is built next, see you soon in the next article.