Entity Framework allows you to extend the DbContext class to add your own entities, extend existing entities, and customize them at runtime. All of these in a clean and simple manner, if you are willing to do so. It doesn’t even matter if those entities don’t have a table representation. You can do that by leveraging C# partial classes and methods. Extending EF Core DbContext is a common scenario, but I think we’re often not aware of its real power.

The way I like to organize entities in my projects

Introducing OnModelCreatingPartial

Commonly your application context will be similar to this: a lot of DbSet<T> that map to the database table object.

    public partial class ConferenceContext : DbContext
    {
        public ConferenceContext(DbContextOptions<ConferenceContext> options)
            : base(options)
        {
            this.Database.EnsureCreated();

        }

        public DbSet<Speaker> Speakers { get; set; }
        public DbSet<Talk> Talks { get; set; }


        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            OnModelCreatingPartial(modelBuilder);
        }
        
        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }

You might notice the OnModelCreatingPartial method in the example above. I’ve noticed this recently after using a database-first approach in a small project. To be honest, it was really an “Aha!” moment, because if you look into DbContext class source, you will not find a reference to it.

This method gives you a simple and clean way to hook into OnModelCreating method to add customization for the extra-entities you might need.
If you are like me you keep an eye on performance or data transfer from the database into the project, you will use raw SQL queries. As a result, you will end up with many classes used only for specific scenarios.

Before EF Core 3.0 there was another datatype DbQuery that allowed you to specify that the entity T is used only for querying and not for inserts or other operations. Migrations and idempotent scripts generated are done in the same way, but above EF Core 3.0, everything is a DBSet<T>.

It is up to you to tell EF Core that T has no key, like in the example below. You can read more about keyless entity types and the future of this in the Microsoft docs. As you see. extending EF Core DbContext is easy and you can use the approach you are comfortable with either with FluentApi or annotations.

    public partial class ConferenceContext
    {
        public DbSet<TicketSummary> TicketSummaries { get; set; }

        public void OnModelCreatingPartial(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<TicketSummary>().HasNoKey();
           
        }
    }

In this example, TicketSummary class is used to retrieve results from a raw SQL query. In that query, I make a join and cherry-pick a few columns. That is it. I have no such table called TicketSummary in my database. Instead, I used FluentApi to specify that there is no key for the entity.