Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can DbContext be composed instead of inherited?

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?

like image 732
rexcfnghk Avatar asked Mar 03 '17 01:03

rexcfnghk


People also ask

What are the disadvantages of using static DbContext?

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.

What is the difference between DbSet and DbContext?

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!

Is creating DbContext expensive?

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.

Should you use using with DbContext?

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.


1 Answers

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 contains DbSet<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 DbSets 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

like image 94
Nkosi Avatar answered Sep 26 '22 08:09

Nkosi