Let's say I am doing a code-first development for a school and I have a SchoolDbContext
. Most documentation on Entity Framework suggest you derive from DbContext
:
public class SchoolDbContext : DbContext
{
public IDbSet<Student> Students => Set<Student>();
}
But my argument is SchoolDbContext
is never a specialisation of DbContext
, it is instead just making use of DbContext
so in my opinion, SchoolDbContext
should be composed of DbContext
:
public class SchoolDbContext
{
private readonly DbContext _dbContext;
public SchoolDbContext(DbContext dbContext)
{
_dbContext = dbContext;
}
public IDbSet<Student> Students => _dbContext.Set<Student>();
}
In my composition root of an ASP.NET MVC application, I tried this composition approach by setting up my dependencies like this (using Simple Injector as an example):
private static void RegisterDependencies(Container container)
{
// Assume I have a connection string SchoolDbContext
container.Register(() => new DbContext("SchoolDbContext"), Lifestyle.Scoped);
container.Register<SchoolDbContext>(Lifestyle.Scoped);
}
Web.config:
<connectionStrings>
<add name="SchoolDbContext"
providerName="System.Data.SqlClient"
connectionString="Server=(localdb)\MSSQLLocalDB;Integrated Security=true"/>
</connectionStrings>
This fails when I try to load Students
with:
The entity type Student is not part of the model for the current context.
When I change it back to the inheritance approach (i.e. invoking the default constructor new SchoolDbContext()
) everything works perfectly.
Is my composition approach not supported by Entity Framework?
If you only use one DbContext instance and lock it for thread safety, so you only have one connection to DB. If your website have hundreds of request per second then all of them have to queue to use the only connection. In that case, DbContext object became the perfomance bottleneck of your system.
Intuitively, a DbContext corresponds to your database (or a collection of tables and views in your database) whereas a DbSet corresponds to a table or view in your database. So it makes perfect sense that you will get a combination of both!
The first time a DbContext is created is pretty expensive but once this has been done a lot of the information is cached so that subsequent instantiations are a lot quicker.
EF and EF Core DbContext types implement IDisposable . As such, best practice programming suggests that you should wrap them in a using() block (or new C# 8 using statement). Unfortunately, doing this, at least in web apps, is generally a bad idea.
Can DbContext be composed instead of inherited?
Short answer: NO
To quote the remarks from official documentation (emphasis mine)
DbContext
is usually used with a derived type that containsDbSet<TEntity>
properties for the root entities of the model. These sets are automatically initialized when the instance of the derived class is created....
There is more but it is too much to put in this answer.
Source DbContext Class
Is my composition approach not supported by Entity Framework?
If you look at the source code for DbContext
there are internal methods that search the class for DbSet
s and initializes them.
Mainly this section of code
/// <summary>
/// Initializes the internal context, discovers and initializes sets, and initializes from a model if one is provided.
/// </summary>
private void InitializeLazyInternalContext(IInternalConnection internalConnection, DbCompiledModel model = null)
{
DbConfigurationManager.Instance.EnsureLoadedForContext(GetType());
_internalContext = new LazyInternalContext(
this, internalConnection, model
, DbConfiguration.GetService<IDbModelCacheKeyFactory>()
, DbConfiguration.GetService<AttributeProvider>());
DiscoverAndInitializeSets();
}
/// <summary>
/// Discovers DbSets and initializes them.
/// </summary>
private void DiscoverAndInitializeSets()
{
new DbSetDiscoveryService(this).InitializeSets();
}
Any model you try to get will throw the same error as the model was not part of the context when it was initialized which can only happen if it was a member of a derived class.
DbContext source code on Github
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With