IT:AD:Code First:DbContext

Summary

An EF DbContext is an “occasionally-connected”, “in-memory” representation of the remote database.

In other words, in its simplest form, a DbContext is rather like a DataSet of the whole database, keeping track of changes made to its in-mem tables, and only committing those changes back to the database when the Commit() method is called.

The recipe for creating an application specific DbContext is as follows.

  • Create an in-app class that derives from DbContext (it's an abstract class).
  • Create an appropriate constructor (see: About the ConnectionString/)
  • Choose an initialization strategy (see About Initialization/) and optionally deal with Seeding concerns at the same time.
  • Optionally provide one or more DbSet<TEntity> representations of database tables.


    public class TestContext : DbContext {
    
       //See: http://bit.ly/t7r4pS
          public TestContext():base("OverrideWithCustomConnStrNameThatDiffersFromExpectedTestContex") {}

          public DbSet<TestEntity> TestEntities { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder) {

           //Keep tables as TestEntity -- not TestEntities
           modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            //modelBuilder.Entity<TEstEntity>()
            //  .HasRequired(a => a.Source)
            //  .WithMany()
            //  .HasForeignKey(a => a.SourceId);
  
        }
    }
    

As stated above, the most common common convention is to provide an argument-less constructor (note that an argumentless constructor is required for Code migrations from a IT:AD:Continuous Integration point of view).

The reasoning behind this is that by default the DbContext finds its connection string by adhering to the Convention over Configuration/ pattern:

  <add name="ConnStrName"
     connectionString="Data Source=.;Initial Catalog=SomeDb;Integrated Security=True"
     providerName="System.Data.SqlClient"
     />

Note that it actually looks for the best solution in several places: http://bit.ly/t7r4pS

For those who have a need to go beyond Convention (really?), one can override the ConnectionString with a custom name (see below) or passing a specific ConnectionString (it will tell the difference by the fact that ConnectionString's have a '=' in them somewhere.
But before breaking Conventions, consider that they they are there for a reason. For one, they're cheaper to maintain among a team.

The first call to the DbContext implementation during the lifespan of the application will invoke the IDatabaseInitializer associated to the database.

There are various IDatabaseInitializer, but I think they all check to see if the database schema has changed before proceeding. They do this by querying the schema from the database, and comparing this against an in-mem map of what your code says the database is. You provide the in-mem description of the map within the OnModelCreating event handler. Depending on the flavour of the IDatabaseInitializer invoked, if the two don't match, it may decide to upgrade the db, using the model you provided within the OnModelCreating event handler.

The Model is built up using IT:AD:Code First:FluentAPI syntax.

Once the model check within the IDatabaseInitializer stage is out of the way (it's only relevant for the first call of an app), you refer to Database tables by exposing DbSet<T> properties of those entities (exposing entities that are not described within the OnModelCreating event handler will obviously create an error).

Note that unlike ModelFirst or DatabaseFirst (which expect EntityConnection), CodeFirst expects just a ConnectionString.

  • /home/skysigal/public_html/data/pages/it/ad/code_first/dbcontext.txt
  • Last modified: 2023/11/04 03:22
  • by 127.0.0.1