Introduction to ABP Series (6): Database Configuration

2020年9月16日 94点热度 1人点赞 0条评论
内容目录

Introduction to ABP Series (6): Database Configuration

Copyright © Author: whuanle, reproduction of articles from WeChat public account requires consent from "NCC Open Source Community".

The previous article in this series: https://www.cnblogs.com/whuanle/p/13061059.html

Since this part belongs to the basic section of ASP.NET Core and is not encapsulated by ABP, there’s not much to say.

In this article, we will learn how to add database configurations in ABP and outline a simple database module structure. We will be using EFCore + Freesql to build the database module.

Freesql is highly recommended! Freesql is an ORM framework produced by Teacher Ye and is now a member project of NCC. It has resolved many pain points I face in daily development, and its consideration for business development, along with numerous extensibility features, makes it truly indispensable to me!

In AbpBase.Database, add the following libraries via Nuget:

All versions are 1.9.0-preview0917; you can use the latest versions.

Freesql
FreeSql.Provider.Sqlite
FreeSql.Provider.SqlServer
FreeSql.Provider.MySql

Creating a Standard EFCore Database Context

In ABP, the EFCore context class needs to inherit from AbpDbContext<T>, and the overall writing method is consistent with inheriting DbContext<T>. Next, we will explain step by step how to add EFCore functionality in AbpBase.

Connection String

In ABP, you can add a ConnectionStringName attribute to the context class. When configuring services, ABP will automatically configure the connection string for it.

    [ConnectionStringName("Default")]
    public partial class DatabaseContext : AbpDbContext<DatabaseContext>

Default is an identifier, and you can use other string identifiers as well.

Defining Isolated Contexts

First, we will create two folders in the AbpBase.Database module:

BaseData
ExtensionData

The BaseData directory is used to store contexts for basic table structures, while ExtensionData is meant for tables that may have extensions or frequently change.

In BaseData, create a class named AbpBaseDataContext with the following content:

using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;

namespace AbpBase.Database
{
    /// <summary>
    /// Context
    /// <para>This section is used to define and configure mappings for basic tables</para>
    /// </summary>
    [ConnectionStringName("Default")]
    public partial class AbpBaseDataContext : AbpDbContext<AbpBaseDataContext>
    {
        #region Define DbSet<T>

        #endregion

        public AbpBaseDataContext(DbContextOptions<AbpBaseDataContext> options)
        : base(options)
        {
        }

        /// <summary>
        /// Define mappings
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            #region Define Mappings

            #endregion

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}

In ExtensionData, create an identical AbpBaseDataContext class with the following content:

using Microsoft.EntityFrameworkCore;

namespace AbpBase.Database
{
    public partial class AbpBaseDataContext
    {
        #region Define DbSet<T>

        #endregion

        /// <summary>
        /// Define mappings
        /// </summary>
        /// <param name="modelBuilder"></param>
        partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
        {
        }
    }
}

The partial class, where the former is used to define those very basic, core entities (tables) and their mappings, and the latter is used to define structures that are likely to undergo multiple modifications during design when some degree of flexibility is felt.

Multi-Database Support and Configuration

Here, we will configure and inject the context so that the program can support multiple databases.

In the AbpBase.Domain.Shared project, create an enum with the following content:

namespace AbpBase.Domain.Shared
{
    public enum AbpBaseDataType
    {
        Sqlite = 0,
        Mysql = 1,
        Sqlserver = 2

        // Other databases
    }
}

Next, create a WholeShared class with the following content:

namespace AbpBase.Domain.Shared
{
    /// <summary>
    /// Global shared content
    /// </summary>
    public static class WholeShared
    {
        // Database connection properties can be defined in configuration files; here, fixed values are provided just for demonstration.

        /// <summary>
        /// Database connection string
        /// </summary>
        public static readonly string SqlConnectString = "";

        /// <summary>
        /// The type of database to use
        /// </summary>
        public static readonly AbpBaseDataType DataType = AbpBaseDataType.Sqlite;
    }
}

Then, add dependency injection in the ConfigureServices function of the AbpBaseDatabaseModule module:

context.Services.AddAbpDbContext<AbpBaseDataContext>();

There’s no need to configure the database connection string here; it can be configured later using some methods provided by ABP.

Configure the context connection string:

            string connectString = default;
            Configure<AbpDbConnectionOptions>(options =>
            {
                connectString = WholeShared.SqlConnectString;
                options.ConnectionStrings.Default = connectString;
            });

Configure multi-database support:

            FreeSql.DataType dataType = default;

            Configure<AbpDbContextOptions>(options =>
            {
                switch (WholeShared.DataType)
                {
                    case AbpBaseDataType.Sqlite:
                        options.UseSqlite<AbpBaseDataContext>(); dataType = FreeSql.DataType.Sqlite; break;
                    case AbpBaseDataType.Mysql:
                        options.UseMySQL<AbpBaseDataContext>(); dataType = FreeSql.DataType.MySql; break;
                    case AbpBaseDataType.Sqlserver:
                        options.UseSqlServer<AbpBaseDataContext>(); dataType = FreeSql.DataType.SqlServer; break;
                }
            });

This completes the multi-database configuration for EFCore.

Next, let’s configure Freesql similarly.

Freesql Configuration Service

First, there are several configuration methods within Freesql. Readers can refer to the Wiki to learn about Freesql: https://github.com/dotnetcore/FreeSql/wiki/%E5%85%A5%E9%97%A8

The author of this article uses a “non-formal” design approach, ha ha ha.

In the BaseData directory, create a class called FreesqlContext with the following content:

using FreeSql.Internal;
using System;
using System.Collections.Generic;
using System.Text;

namespace AbpBase.Database
{
    /// <summary>
    /// Freesql Context
    /// </summary>
    public partial class FreesqlContext
    {
        public static IFreeSql FreeselInstance => Freesql_Instance;
        private static IFreeSql Freesql_Instance;

        public static void Init(string connectStr, FreeSql.DataType dataType = FreeSql.DataType.Sqlite)
        {
            Freesql_Instance = new FreeSql.FreeSqlBuilder()
                .UseNameConvert(NameConvertType.PascalCaseToUnderscore)
                .UseConnectionString(dataType, connectStr)

                //.UseAutoSyncStructure(true) // Automatically sync entity structures to the database; prohibited in production environments!

                .Build();
            OnModelCreating(Freesql_Instance);
        }

        private static void OnModelCreating(IFreeSql freeSql)
        {
            OnModelCreatingPartial(freeSql);
        }
    }
}

In the ExtensionData directory, create a FreesqlContext class with the following content:

using FreeSql;
using System;
using System.Collections.Generic;
using System.Text;

namespace AbpBase.Database
{
    public partial class FreesqlContext
    {
        private static void OnModelCreatingPartial(IFreeSql freeSql)
        {
            var modelBuilder = freeSql.CodeFirst;

            SyncStruct(modelBuilder);
        }

        /// <summary>
        /// Sync structure to the database
        /// </summary>
        /// <param name="codeFirst"></param>
        private static void SyncStruct(ICodeFirst codeFirst)
        {
            // codeFirst.SyncStructure(typeof(user));
        }
    }
}

Then, in the ConfigureServices function of AbpBaseDatabaseModule, add the injected services:

            FreesqlContext.Init(connectString, dataType);
            context.Services.AddSingleton(typeof(IFreeSql), FreesqlContext.FreeselInstance);
            context.Services.AddTransient(typeof(FreesqlContext), typeof(FreesqlContext));

Through the above steps, our ABP can now support multiple databases, EFCore + Freesql, while isolating the maintenance of tables.

痴者工良

高级程序员劝退师

文章评论