Data

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

  1. say I add a new property to Post class
  2. Add-Migration UpdatePost creates a migration named UpdatePost prefixed with timestamp, it contains the new property to add to the Blog_Post table
  3. Update-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.

Fanray Database Schema

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.

Widget
Settings