7.8 KiB
EF Core Code First Migrations
Entity Framework Core provides an easy to use and powerful database migration system. ABP Framework startup templates take the advantage of this system to allow you to develop your application in a standard way.
See its own documentation to understand the EF Core Code First Migrations and why you need to such a system.
However, EF Core migration system is not so good in a modular environment where each module maintains its own database schema while two or more modules may share a single database in practical.
Since ABP Framework cares about modularity in all aspects, it provides a solution to this problem. It is important to understand this solution if you need to customize your database structure.
This document begins by introducing the default structure provided by the application startup template and discusses various scenarios you may want to implement for your own application.
The Default Solution & Database Configuration
When you create a new web application (with EF Core, which is the default database provider), your solution structure will be similar to the picture below:
Actual solution structure may be a bit different based on your preferences, but the database part will be same.
The Database Structure
The startup template has some application modules pre-installed. Each layer of the solution has corresponding module package references. So, the .EntityFrameworkCore
project has the NuGet references for the .EntityFrameworkCore
packages of the used modules:
In this way, you collect all the EF Core dependencies under the .EntityFrameworkCore
project.
In addition to the module references, it references to the
Volo.Abp.EntityFrameworkCore.SqlServer
package since the startup template is pre-configured for the SQL Server. See the documentation if you want to switch to another DBMS.
While every module has its own DbContext
class by design and can use its own physical database, the solution is configured to use a single shared database as shown in the figure below:
This is the simplest configuration and suitable for most of the applications. appsettings.json
file has a single connection string, named Default
:
"ConnectionStrings": {
"Default": "..."
}
So, you have a single database schema which contains all the tables of the modules sharing this database.
ABP Framework's connection string system allows you to easily set a different connection string for a desired module:
"ConnectionStrings": {
"Default": "...",
"AbpAuditLogging": "..."
}
The example configuration about tells to the ABP Framework to use the second connection string for the Audit Logging module.
However, this is just the beginning. You also need to create the second database, create audit log tables inside it and maintain the database tables using the code first approach. One of the main purposes of this document is to guide you on such database separation scenarios.
Module Tables
Every module uses its own databases tables. For example, the Identity Module has some tables to manage the users and roles in the system.
Table Prefixes
Since it is allowed to share a single database by all modules (it is the default configuration), a module typically uses a prefix to group its own tables.
The fundamental modules, like Identity, Tenant Management and Audit Logs, use the Abp
prefix, while some other modules use their own prefixes. Identity Server module uses the IdentityServer
prefix for example.
If you want, you can change the database table name prefix for a module for your application. Example:
Volo.Abp.IdentityServer.AbpIdentityServerDbProperties.DbTablePrefix = "Ids";
This code changes the prefix of the Identity Server module. Write this code at the very beginning in your application.
Every module also defines
DbSchema
property (near toDbTablePrefix
), so you can set it for the databases support the schema usage.
The Projects
From the database point of view, there are three important projects those will be explained in the next sections.
.EntityFrameworkCore Project
This project has the DbContext
class (BookStoreDbContext
for this sample) of your application. You typically use this DbContext
in your application code (in your custom repositories if you follow the best practices).
It is almost an empty DbContext
since your application don't have any entities at the beginning, except the pre-defined AppUser
entity:
[ConnectionStringName("Default")]
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
{
public DbSet<AppUser> Users { get; set; }
/* Add DbSet properties for your Aggregate Roots / Entities here. */
public BookStoreDbContext(DbContextOptions<BookStoreDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Configure the shared tables (with included modules) here */
builder.Entity<AppUser>(b =>
{
//Sharing the same table "AbpUsers" with the IdentityUser
b.ToTable("AbpUsers");
//Configure base properties
b.ConfigureByConvention();
b.ConfigureAbpUser();
//Moved customization of the "AbpUsers" table to an extension method
b.ConfigureCustomUserProperties();
});
/* Configure your own tables/entities inside the ConfigureBookStore method */
builder.ConfigureBookStore();
}
}
This simple DbContext
class still needs some explanations:
- It defines a
[ConnectionStringName]
attribute which tells ABP to always use theDefault
connection string for thisDbcontext
. - It inherits from the
AbpDbContext<T>
instead of the standardDbContext
class. You can see the EF Core integration document for more. For now, know that theAbpDbContext<T>
base class implements some conventions of the ABP Framework to automate some common tasks for you. - It declares a
DbSet
property for theAppUser
entity.AppUser
shares the same table (namedAbpUsers
by default) with theIdentityUser
entity of the Identity module. The startup template provides this entity inside the application since we think that the User entity is generally needs to be customized in your application. - The constructor takes a
DbContextOptions<T>
instance. - It overrides the
OnModelCreating
method to define the EF Core mappings.- It first calls the the
base.OnModelCreating
method to let the ABP Framework to implement the base mappings for us. - It then configures the mapping for the
AppUser
entity. There is a special case for this entity, which will be explained in the next sections. - It finally calls the
builder.ConfigureBookStore()
extension method to configure other entities of your application.
- It first calls the the
This design will be explained in more details after introducing the other database related projects.
.EntityFrameworkCore.DbMigrations Project
TODO