Fanray uses Entity Framework Core Code First for data access.
DbContext
There is only one DbContext FanDbContext
for the entire solution in the base class library Fan
. Each application built on top will have their model classes locally in their projects.
Each model class that results into a database table needs to derive from the Entity
class. Upon app initial launch FanDbContext
scans all the DLLs to look for entities that are derived from this class along with Migrations to apply changes to your database.
public class Entity
{
public int Id { get; set; }
}
Every application that needs to persist data needs an Entity Model Builder that implements the IEntityModelBuilder
interface.
public interface IEntityModelBuilder
{
/// <summary>
/// Creates the entity model for the project.
/// </summary>
/// <param name="builder"></param>
void CreateModel(ModelBuilder builder);
}
In a typical Entity Model Builder class, you will find database table name mappings to entity classes, relations, indexes, constraints etc. For example, here is the BlogEntityModelBuilder
class in the Fan.Blog
project.
public class BlogEntityModelBuilder : IEntityModelBuilder
{
public void CreateModel(ModelBuilder builder)
{
builder.Entity<Post>(entity =>
{
entity.ToTable("Blog_Post");
entity.HasIndex(e => e.Slug);
entity.HasIndex(e => new { e.Type, e.Status, e.CreatedOn, e.Id });
entity.HasIndex(e => e.ParentId);
entity.HasIndex(e => e.UserId);
});
builder.Entity<Category>(entity =>
{
entity.ToTable("Blog_Category");
entity.HasKey(e => e.Id).IsClustered(clustered: false);
entity.HasIndex(e => e.Slug).IsUnique().IsClustered();
});
builder.Entity<Tag>(entity =>
{
entity.ToTable("Blog_Tag");
entity.HasKey(e => e.Id).IsClustered(clustered: false);
entity.HasIndex(e => e.Slug).IsUnique().IsClustered();
});
builder.Entity<PostTag>(entity =>
{
entity.ToTable("Blog_PostTag");
entity.HasKey(e => new { e.PostId, e.TagId });
});
}
}
Migrations
EF migrations are inside the Fan project’s Migrations
folder. There is one migration per release, for Fanray v1.1 there would be two migrations one for v1.0 and one for v1.1. A typical workflow to add a migration is like this
- say I add a new property to
Post
class Add-Migration UpdatePost
creates a migration namedUpdatePost
prefixed with timestamp, it contains the new property to add to theBlog_Post
tableUpdate-Database -Migration FanV1
applies changes since FanV1.
Before a release there may be a bunch of these interim small changes, I then manually merge them into a single migration for that release.
Not everyone likes their production database updated by EF migrations, thus I also provide you with a migration .sql script to update your database. These upgrade scripts are inside the sql
folder in the solution. To generate an upgrade .sql script from a migration, do the following
# generates entire script
Script-Migration
# generates a script since the v1.0, `-Idempotent` makes your script re-executable
Script-Migration -Idempotent -From FanV1_1
I’m contemplating eventually to keep just one migration around at some point, in which case using the upgrade .sql script is the only way to upgrade your database from version to version.
Schema
The Core_
prefixed tables are provided by Asp.net Core Identity, see CoreEntityModelBuilder
class in the Fan
project for more info.
Repositories
The data access layer uses Repository Pattern. It provides a IRepository<T>
interface and EntityRepository<T>
base class that implements that interface with default implementations on common CRUD operations. Each individual repository can override these methods and provide additional methods.