從壹開始先後端分離[.NetCore ] 38 ║自動初始化數據庫(不按期更新)

緣起

哈嘍你們好呀,咱們又見面啦,這裏先祝你們聖誕節快樂喲,昨天的紅包不知道有沒有小夥伴搶到呢。今天的這篇內容灰常簡單,只是對咱們的系統的數據庫進行CodeFirst,而後就是數據處理,由於這幾個月來,仍是有小夥伴陸陸續續的向我索要數據,原本想着都是很簡單的數據,就不給了,而後僅僅是提供了一個Sql的表結構,可是想一想一個完整的項目,怎麼能沒有一個初始化的功能呢(不只僅是表結構,仍是要一些簡單的數據)?因此就想着寫今天這篇文章了,這篇文章雖然可能看起來很簡單,不過也是給你們提供了一個思路,就是本身之後在寫項目的時候,如何添加一個初始化的Seed Data,我也是參考其餘小夥伴的,這裏重點表揚下QQ羣裏,@初久童鞋,沒有他的博客園地址,就沒辦法放他的首頁了。html

投稿做者:初久,我的地址:null,實現項目啓動的時候,自動初始化數據,其中會涉及到上下文、SqlSugar、依賴注入等知識。git

好啦,話很少說,直接開始動手。github

 

1、對Mode實體類進行配置

由於要使用到了CodeFirst了,因此咱們必需要對咱們的實體類 Model 進行配置,固然也有不少的小夥伴,使用的是EFCore,固然是能夠的,EFCore不須要對實體類進行處理,只是須要額外的配置上下文和Map映射,好比這樣:web

 

EFCore的我就很少少了,很簡單,若是有不會的小夥伴,能夠看個人第二個系列的《讓你明白DDD的小故事 & EFCore初探》和《剪不斷理還亂的 值對象和Dto》這兩篇文章都有對EFCore的配置有提到,有須要的能夠看看。數據庫

由於本系列是用的SqlSugar ORM,因此就來講說,它是如何配置的,那我們就配置下咱們的SqlSugar吧。api

這裏只用 Advertisement.cs 來舉例吧,其餘的,你們能夠自行去查看個人Github上的code:網絡

    public class Advertisement : RootEntity
    {

        /// <summary>
        /// 廣告圖片
        /// </summary>
        [SugarColumn(Length = 512, IsNullable = true)]
        public string ImgUrl { get; set; }

        /// <summary>
        /// 廣告標題
        /// </summary>
        [SugarColumn(Length = 64, IsNullable = true)]
        public string Title { get; set; }

        /// <summary>
        /// 廣告連接
        /// </summary>
        [SugarColumn(Length = 256, IsNullable = true)]
        public string Url { get; set; }

        /// <summary>
        /// 備註
        /// </summary>
        [SugarColumn(Length = int.MaxValue, IsNullable = true)]
        public string Remark { get; set; }

        /// <summary>
        /// 建立時間
        /// </summary>
        public DateTime Createdate { get; set; } = DateTime.Now;
    }

    public class RootEntity
    {
        /// <summary>
        /// ID
        /// </summary>
        [SugarColumn(IsNullable = false, IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
    }

 

你們能夠看到,SqlSugar 和 EFCore在操做上仍是不同的,sugar不須要配置額外的Map 映射,只須要對當前類進行操做,不過仍是有不少小夥伴反映,仍是EFCore 在使用上或者在功能上更健壯,這裏就很少說兩者了,今天的主題是數據自動初始化,不能本末倒置了。多線程

 

這個就很簡單的了,主要就是屬性 SugarColumn() ,裏邊有一些屬性,能夠自行配置,這裏給你們簡單註釋一下:app

 public class SugarColumn : Attribute
 {
     public SugarColumn();

     public string ColumnName { get; set; }//列名
     public bool IsIgnore { get; set; }//是否忽略
     public bool IsPrimaryKey { get; set; }//是不是主鍵
     public bool IsIdentity { get; set; }//是否自增
     public string MappingKeys { get; set; }//映射key
     public string ColumnDescription { get; set; }//列描述
     public int Length { get; set; }//長度
     public bool IsNullable { get; set; }//是否爲空
     public string OldColumnName { get; set; }//舊的列名
     public string ColumnDataType { get; set; }//列類型,自定義
     public int DecimalDigits { get; set; }//dicimal精度
     public string OracleSequenceName { get; set; }//Oracle序列名
     public bool IsOnlyIgnoreInsert { get; set; }//是否僅對添加忽略
     public bool IsEnableUpdateVersionValidation { get; set; }
 }

 

這裏我已經配置完成了,並且是儘可能的仿照着個人數據庫來的,可能會有細微的差異,若是你要想使用的話,能夠用一個測試的數據庫來實驗ssh

 

2、配置上下文與初始數據

 你們是否還記得以前在倉儲Repository中,咱們建立了一個上下文,這裏能夠直接拿來用,不過由於咱們的 API 層已經和 Repository 層解耦分割了,因此我就在 Mode 層來實現這個功能吧。若是你不解耦,能夠直接使用倉儲層的上下文便可。

一、創建 SqlSugar 上下文

在 Blog.Core.Model 層新建一個 Seed 文件夾,而後把倉儲層中的 context 拷貝過去,我重命名爲 MyContext.cs:

 

重點就是構造函數,要實現實例化 SqlSugarClient 的做用:

 public MyContext()
 {
     if (string.IsNullOrEmpty(_connectionString))
         throw new ArgumentNullException("數據庫鏈接字符串爲空");
     _db = new SqlSugarClient(new ConnectionConfig()
     {
         ConnectionString = _connectionString,//數據庫字符串
         DbType = DbType.SqlServer,//數據庫類型
         IsAutoCloseConnection = true,//自動關閉數據庫
         IsShardSameThread = false,//啓用異步多線程
         InitKeyType = InitKeyType.Attribute,//mark
         ConfigureExternalServices = new ConfigureExternalServices()
         {
             //DataInfoCacheService = new HttpRuntimeCache()
         },
         MoreSettings = new ConnMoreSettings()
         {
             //IsWithNoLockQuery = true,
             IsAutoRemoveDataCache = true
         }
     });
 }

 

 

二、實現初始化種子數據的功能

 上邊我們建立了好上下文,那接下來我們就應該實現 CodeFirst 功能了,

仍是再 Seed 文件夾,新建 DBSeed.cs 類:

    public class DBSeed
    {
        /// <summary>
        /// 異步添加種子數據
        /// </summary>
        /// <param name="myContext"></param>
        /// <returns></returns>
        public static async Task SeedAsync(MyContext myContext)
        {
            try
            {
// 注意!必定要先手動建立一個空的數據庫,5.x 版本會自動建立數據庫了
// 會覆蓋,能夠設置爲true,來備份數據 // 若是生成過了,第二次,就不用再執行一遍了,註釋掉該方法便可 myContext.CreateTableByEntity(false, typeof(Advertisement), typeof(BlogArticle), typeof(Guestbook), typeof(Module), typeof(ModulePermission), typeof(OperateLog),
typeof(PasswordLib), typeof(Permission), typeof(Role), typeof(RoleModulePermission), typeof(sysUserInfo), typeof(Topic), typeof(TopicDetail), typeof(UserRole));
// 下邊的就是種子數據
#region Advertisement if (!await myContext.Db.Queryable<Advertisement>().AnyAsync()) { myContext.GetEntityDB<Advertisement>().Insert( new Advertisement() { Createdate = DateTime.Now, Remark = "mark", Title = "good" }); } #endregion #region BlogArticle Guestbook if (!await myContext.Db.Queryable<BlogArticle>().AnyAsync()) { int bid = myContext.GetEntityDB<BlogArticle>().InsertReturnIdentity( new BlogArticle() { bsubmitter = "admins", btitle = "老張的哲學", bcategory = "技術博文", bcontent = "<p>1。。。。。。", btraffic = 1, bcommentNum = 0, bUpdateTime = DateTime.Now, bCreateTime = DateTime.Now }); if (bid > 0) { if (!await myContext.Db.Queryable<Guestbook>().AnyAsync()) { myContext.GetEntityDB<Guestbook>().Insert( new Guestbook() { blogId = bid, createdate = DateTime.Now, username = "user", phone = "110", QQ = "100", body = "很不錯", ip = "127.0.0.1", isshow = true, }); } } } #endregion #region Module int mid = 0; if (!await myContext.Db.Queryable<Module>().AnyAsync()) { mid = myContext.GetEntityDB<Module>().InsertReturnIdentity( new Module() { IsDeleted = false, Name = "values的接口信息", LinkUrl = "/api/values", OrderSort = 1, IsMenu = false, Enabled = true, }); } #endregion #region Role int rid = 0; if (!await myContext.Db.Queryable<Role>().AnyAsync()) { rid = myContext.GetEntityDB<Role>().InsertReturnIdentity( new Role() { IsDeleted = false, Name = "Admin", Description = "我是一個admin管理員", OrderSort = 1, CreateTime = DateTime.Now, Enabled = true, ModifyTime = DateTime.Now }); } #endregion #region RoleModulePermission if (mid > 0 && rid > 0) { if (!await myContext.Db.Queryable<RoleModulePermission>().AnyAsync()) { myContext.GetEntityDB<RoleModulePermission>().Insert( new RoleModulePermission() { IsDeleted = false, RoleId = rid, ModuleId = mid, CreateTime = DateTime.Now, ModifyTime = DateTime.Now }); } } #endregion #region sysUserInfo int uid = 0; if (!await myContext.Db.Queryable<sysUserInfo>().AnyAsync()) { uid = myContext.GetEntityDB<sysUserInfo>().InsertReturnIdentity( new sysUserInfo() { uLoginName = "admins", uLoginPWD = "admins", uRealName = "admins", uStatus = 0, uCreateTime = DateTime.Now, uUpdateTime = DateTime.Now, uLastErrTime = DateTime.Now, uErrorCount = 0 }); } #endregion #region UserRole if (uid > 0 && rid > 0) { if (!await myContext.Db.Queryable<UserRole>().AnyAsync()) { myContext.GetEntityDB<UserRole>().Insert( new UserRole() { IsDeleted = false, UserId = uid, RoleId = rid, CreateTime = DateTime.Now, ModifyTime = DateTime.Now }); } } #endregion #region Topic TopicDetail if (!await myContext.Db.Queryable<Topic>().AnyAsync()) { int tid = myContext.GetEntityDB<Topic>().InsertReturnIdentity( new Topic() { tLogo = "/Upload/20180626/95445c8e288e47e3af7a180b8a4cc0c7.jpg", tName = "《羅馬人的故事》", tDetail = "這是一個蕩氣迴腸的故事", tIsDelete = false, tRead = 0, tCommend = 0, tGood = 0, tCreatetime = DateTime.Now, tUpdatetime = DateTime.Now, tAuthor = "laozhang" }); if (tid > 0) { if (!await myContext.Db.Queryable<TopicDetail>().AnyAsync()) { myContext.GetEntityDB<TopicDetail>().Insert( new TopicDetail() { TopicId = tid, tdLogo = "/Upload/20180627/7548de20944c45d48a055111b5a6c1b9.jpg", tdName = "第一章 羅馬的誕生 第一節 傳說的年代", tdContent = "<p>第一節 傳說的年代</時代走出,近入了歷史時代。</p><p><br></p>", tdDetail = "第一回", tdIsDelete = false, tdRead = 1, tdCommend = 0, tdGood = 0, tdCreatetime = DateTime.Now, tdUpdatetime = DateTime.Now, tdTop = 0, }); } } } #endregion } catch (Exception ex) { } } }

 

 是否是很簡單,上邊的 CreateTableByEntity 是用來建立數據庫的表結構的,第一次執行完成後,剩下的就能夠不用執行了。下邊的是添加種子數據,我增長了判斷,其餘的你們能夠自定義處理。

這個時候咱們已經把初始化表結構,和添加種子數據完成了,那咱們應該怎麼用呢,別慌,請往下看。

 

3、在項目啓動的時候,執行初始化

一、將上邊的類注入服務

 這個很簡單,相信你們都能看懂,我就直接注入到服務,而後服務會自動注入到Autofac:

 

二、在主程序 Main 中啓動初始化

 相信你們都應該知道,其實 .net core 自己是一個控制檯程序,因此項目啓動是在 Program.cs 中的 Main主程序方法中的,咱們作一下修改:

    public class Program
    {
        public static void Main(string[] args)
        {
            // 生成承載 web 應用程序的 Microsoft.AspNetCore.Hosting.IWebHost。Build是WebHostBuilder最終的目的,將返回一個構造的WebHost。
            var host = CreateWebHostBuilder(args).Build();

            // 建立可用於解析做用域服務的新 Microsoft.Extensions.DependencyInjection.IServiceScope。
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                var loggerFactory = services.GetRequiredService<ILoggerFactory>();

                try
                {
                    // 從 system.IServicec提供程序獲取 T 類型的服務。
                    var myContext = services.GetRequiredService<MyContext>();
                    DBSeed.SeedAsync(myContext).Wait();
                }
                catch (Exception e)
                {
                    var logger = loggerFactory.CreateLogger<Program>();
                    logger.LogError(e, "Error occured seeding the Database.");
                }
            }

            // 運行 web 應用程序並阻止調用線程, 直到主機關閉。
            // 建立完 WebHost 以後,便調用它的 Run 方法,而 Run 方法會去調用 WebHost 的 StartAsync 方法
            // 將Initialize方法建立的Application管道傳入以供處理消息
            // 執行HostedServiceExecutor.StartAsync方法
            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            //使用預配置的默認值初始化 Microsoft.AspNetCore.Hosting.WebHostBuilder 類的新實例。
            WebHost.CreateDefaultBuilder(args)
                //指定要由 web 主機使用的啓動類型。至關於註冊了一個IStartup服務。
                .UseStartup<Startup>();
    }

 

執行流程就是,咱們項目啓動,首先會建立一個初始化WebHostBuilder 實例,而後使用啓動默認的 Startup 服務,固然你也能夠自定義這個啓動服務,好比 StatupDevelopment

這樣寫  .UseStartup(typeof(StartupDevelopment).GetTypeInfo().Assembly.FullName)

接下來,就是 Build 咱們的剛剛實例化的 webhostbuilder ,生成一個 WebHost 宿主主機。

中間咱們就能夠對宿主下的服務進行配置,

最後就是執行 Run() 方法,啓動應用程序,直到主機關閉。

若是有小夥伴想更多的瞭解 .net core 的啓動配置相關知識,能夠看這裏有一個QQ羣管理Dave 大神的視頻:

https://www.bilibili.com/video/av38392956/?p=2

 

4、測試結果

一、用動圖來演示效果

 通過配置,我這裏先創建了一個空的數據庫 DBInitTest ,而後看看效果:

 

這裏要注意下:根據數據庫大小的不一樣,中間可能經歷的時間不同,咱們已經成功的生成了數據庫,並初始化出來了數據。

好啦,今天的這個小技巧就說到這裏了,你也能夠根據本身的狀況,根據本身的ORM來設計喲,特別適用於一個給別人展現的Demo項目,和本身的小項目。

 

二、若是用EFCore會更簡單

上邊我們說到了,有的小夥伴會使用EFCore,並且上邊我們也簡單說了,在EFCore 進行實體映射之後,就能夠直接進行Code First 和 種子數據初始化了:

官方地址:https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding

 try
 {
     // TODO: Only run this if using a real database
     myContext.Database.Migrate();

     if (!myContext.Posts.Any())
     {
         myContext.Posts.AddRange(
             new List<Post>{
         new Post{
             Title = "Post Title 1",
             Body = "Post Body 1",
             Author = "Dave",
             LastModified = DateTime.Now
         }
             }
         );
         await myContext.SaveChangesAsync();
     }
 }

 

 

     最後,聖誕節快樂

最後來個今天火的不得了的小圖:

(圖片來源於網絡,侵刪)

 

5、Github & Gitee

https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core

 

--END

相關文章
相關標籤/搜索