哈嘍你們好呀,咱們又見面啦,這裏先祝你們聖誕節快樂喲,昨天的紅包不知道有沒有小夥伴搶到呢。今天的這篇內容灰常簡單,只是對咱們的系統的數據庫進行CodeFirst,而後就是數據處理,由於這幾個月來,仍是有小夥伴陸陸續續的向我索要數據,原本想着都是很簡單的數據,就不給了,而後僅僅是提供了一個Sql的表結構,可是想一想一個完整的項目,怎麼能沒有一個初始化的功能呢(不只僅是表結構,仍是要一些簡單的數據)?因此就想着寫今天這篇文章了,這篇文章雖然可能看起來很簡單,不過也是給你們提供了一個思路,就是本身之後在寫項目的時候,如何添加一個初始化的Seed Data,我也是參考其餘小夥伴的,這裏重點表揚下QQ羣裏,@初久童鞋,沒有他的博客園地址,就沒辦法放他的首頁了。html
投稿做者:初久,我的地址:null,實現項目啓動的時候,自動初始化數據,其中會涉及到上下文、SqlSugar、依賴注入等知識。git
好啦,話很少說,直接開始動手。github
由於要使用到了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
你們是否還記得以前在倉儲Repository中,咱們建立了一個上下文,這裏能夠直接拿來用,不過由於咱們的 API 層已經和 Repository 層解耦分割了,因此我就在 Mode 層來實現這個功能吧。若是你不解耦,能夠直接使用倉儲層的上下文便可。
在 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 是用來建立數據庫的表結構的,第一次執行完成後,剩下的就能夠不用執行了。下邊的是添加種子數據,我增長了判斷,其餘的你們能夠自定義處理。
這個時候咱們已經把初始化表結構,和添加種子數據完成了,那咱們應該怎麼用呢,別慌,請往下看。
這個很簡單,相信你們都能看懂,我就直接注入到服務,而後服務會自動注入到Autofac:
相信你們都應該知道,其實 .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 大神的視頻:
通過配置,我這裏先創建了一個空的數據庫 DBInitTest ,而後看看效果:
這裏要注意下:根據數據庫大小的不一樣,中間可能經歷的時間不同,咱們已經成功的生成了數據庫,並初始化出來了數據。
好啦,今天的這個小技巧就說到這裏了,你也能夠根據本身的狀況,根據本身的ORM來設計喲,特別適用於一個給別人展現的Demo項目,和本身的小項目。
上邊我們說到了,有的小夥伴會使用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(); } }
最後來個今天火的不得了的小圖:
(圖片來源於網絡,侵刪)
https://github.com/anjoy8/Blog.Core
https://gitee.com/laozhangIsPhi/Blog.Core
--END