Question / Opinion request:
Database.EnsureCreated()
Ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created. If the database exists, then no effort is made to ensure it is compatible with the model for this context.
I'd like your opinion on how exactly this If it exists check should be interpreted by plugin authors.
The reason I'm asking is this:
Npgsql.EntityFrameworkCore.PostgreSQL #679
In various providers the logic is usually found in src/Storage/Internal/*DatabaseCreator.HasTables()
To illustrate that this check could have varying levels of thoroughness:
@jdvor It is expected to create the database and generate the tables, etc. if the database doesn't exist or if it exists put is completely empty. Usually, EnsureCreated is used in a pair after EnsureDeleted:
C#
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
This can be used in simple prototyping and some basic testing scenarios to quickly cleanup and recreate the database according to the current model. Anything more complex than that should make use of the Migrations system to define what should and should not be changed when ensuring a database exists and is up-to-date.
Note that the current model definition is not sufficient to in general ensure that the database is both up-to-date and functional when starting from some non-empty database. For example, constraints may exist between tables that are in the current model and ones that are not in the current model.
Applications are free to cross multiple schemas, at least for databases that have true schema support, so it would be incorrect to assume that a model only resides inside a different schema.
@jdvor Also, note that EnsureCreated is a high-level functionality that for relational databases composes some lower-level building blocks on the RelationalDatabaseCreator service. You can use these two do things like CreateTables for multiple contexts in the same database, assuming that you have constrained how this is used to be appropriate for your scenario.
To give a bit more context, I think @jdvor is thinking about possibilities for making EnsureCreated() more useful in cases where multiple models are being used on the same database - that method is currently useless in this scenarios. In theory, we could have EnsureCreated() check that at least one table exists from the given model, rather than any table - this would allow us to run EnsureCreated() twice, once for each model, and have both models' tables correctly created.
However, I do see the sense in what @ajcvickers writes - the buildings blocks are there if people want to implement more sophisticated detection/creation logic.
@roji We considered that in the past, but it can make the behavior more confusing for people, especially in cases where multiple models share tables. Or tables are left-over from previous models.
Overall, the behavior here is intentionally very simplistic. The lower-level APIs and Migrations are there for people to build other behaviors as needed.
Makes sense to me...
@ajcvickers would you be so kind and point me to what is the extension point for implementing such customized creation logic? Is it DbContextOptionsBuilder.ReplaceService
@jdvor You can use context.GetService<IRelationalDatabaseCreator>() to get access to the lower-level service.
@ajcvickers that looks exactly like what I need - .CreateTables(), because I know I can create tables in new randomly generated schema name with no side-effects to anything else in the database and use it in a test fixture; discard it afterwards. Thanks a lot.
Most helpful comment
@roji We considered that in the past, but it can make the behavior more confusing for people, especially in cases where multiple models share tables. Or tables are left-over from previous models.
Overall, the behavior here is intentionally very simplistic. The lower-level APIs and Migrations are there for people to build other behaviors as needed.