EntityFramework Core动态加载模型,我们要知道些什么呢?
這篇文章源于一位問我的童鞋:在EntityFramework Core中如何動態加載模型呢?在學習EntityFramwork時關于這個問題已有對應童鞋給出答案,故沒有過多研究,雖然最后解決了這位童鞋提出的問題,但是當我再次深入研究時,發現原來問題遠沒有這么簡單,由此而引申出來的問題值得我花了一點時間去思考,個人感覺很有價值和必要,所以在此做下記錄或許能夠幫助到有需要的童鞋,研究EntityFramework Core動態加載模型的歷程由此而開始,接下來跟隨我的腳步一起去瞧瞧。
我們依然從零開始,創建EF Core 2.x控制臺程序,然后給出本節內容我們需要用到的模型,如往常一樣我們已經用爛了的Blog和Post,如下:
接下來是我們需要用到的上下文,如下:
我們看到上述表名是模型的復數形式,接下來我們查詢博客列表,如下:
以上演示的則是我們一貫的做法,這個時候就有人問了,隨著業務變更,我們都得在上下文中添加多個模型的DbSet屬性,能否避免此重復操作的情況,將我們后續添加的模型動態加載到上下文中去從而提高工作效率讓我們著重關注業務呢?
當然是闊以的,這里我們借助實際場景來說明,我們將模型通常都會放在一個類庫中,比如我們將上述Blog和Post放在如下圖Model類庫中。
接下來我們要做的則是在初始化模型時,獲取模型所在的程序集,然后將該程序集中的模型通過ModelBuilder生成,正常情況下我們是調用如下Entity方法配置模型,如下:
有了如上分析,我們就通過反射獲取上述Entity方法,然后調用通過ModelBuilder調用反射得到的Entity方法,如下:
當然上述加載模型程序集的方式根據我們實際項目情況而定,同時在我們過濾程序集中類型時也同樣如此,比如若是DDD架構,對于倉儲都會封裝一層進行基本操作的倉儲,此時其他模型倉儲必派生于基倉儲,通過基本倉儲模型進行過濾等等。
接下來我們將上下文中添加的DbSet<Blog>和DbSet<Post>給去掉,如下:
然后我們直接通過上下文中的Set方法來查詢數據,如下:
上述我們多添加了一行確保數據庫模型已提前被創建,這是必要的,其背后本質就是通過命令進行遷移,要不然在加載模型時應該會報錯,當然若在Web應用程序中,我們在Configure方法中也同樣添加如下一行:
此時將會拋出上述異常,這是為何呢?這是因為數據庫表名是和如上上下文中我們已經注釋掉的DbSet包含的模型屬性名稱一致
若我們將上述DbSet包含的模型屬性的注釋給去掉,當加載DbSet屬性時將獲取該屬性名稱和我們配置的Schema作為架構名稱(不配置,默認為空),我們通過如下源碼可得知(當然我們通過SQL Server Profiler生成的SQL語句也可得知)
上述我們只是得到最終表的架構和名稱而已,那么默認表名稱是怎樣的呢?當我們查詢時,會從上述DatasetTable類中去獲取表名,如下:
它具體是什么時候調用的呢,我們看如下代碼:
到了這里我們并未看到任何有效的信息,只是將該類中得到的表名和架構設置到ToTable方法中,讓我們從頭開始梳理思路
因為從一開始我們并未通過注解或者Fluent APi去顯式配置表名,所以此時必將走EntityFramework Core的默認約定,思路已經很清晰,最終我們找到獲取表名的方法,如下:
到這里我們看到了獲取表名的方法,我們繼續往下走,看看具體是如何獲取表名的呢?
因為對應類型并未有其派生類,接下來去獲取注解的表名,此時我們也并未通過注解設置表名,到這里我們也能明白若是我們通過注解在對應模型上添加與數據庫表名一致的復數即可解決問題。我們繼續往下走,最后調用獲取默認表名的方法:
首先我們并未設置模型的OwnType,接下來調用方法根據注釋意為:獲取模型是否有定義的導航類型,看到這里時,我認為Post不就是Blog的導航嗎,此方法被暴露出來可供我們調用,當我去驗證時發現結果卻返回false,不禁讓我心生疑竇
var hasDefiningNavigation = context.Model .FindEntityType(typeof(Blog)) .HasDefiningNavigation();既然返回false,接下來繼續往下看ShortName方法,如下:
到這里我們總算明白了,模型類型不為空獲取模型的名稱,經驗證其ShortDisplayName方法返回值就是模型名稱即Blog,所以才拋出最開始異常對象名無效,我們也可通過如下代碼驗證表名是不是Blog
var?mapping?=?context.Model.FindEntityType(typeof(Blog)) .Relational(); var?schema?=?mapping.Schema; var?tableName?=?mapping.TableName;注意:若您是EntityFramework Core 3.x版本上述獲取架構和表名等方式已經修改成直接針對模型的擴展方法。如下:
var?mapping?=?context.Model.FindEntityType(typeof(Blog)); var?schema?=?mapping.GetSchema(); var?tableName?=?mapping.GetTableName();pping.GetTableName();所以對于EF Core而言,默認的表名就是模型名稱,若我們以DbSet屬性暴露模型則以DbSet屬性名稱作為表名,同樣我們也驗證下,我們將最開始注釋掉的DbSet<Blog> Blogs,修改成如下:
public DbSet<Blog> BlogAlias { get; set; }所以若采用動態加載模型,如果數據庫表名就是模型名稱,那么沒毛病,否則我們應該根據項目約定而需要進行相應的修改才行,如最開始給出的數據庫表名為復數為例,此時我們還需修改數據庫表名的約定,在OnModelCreating方法添加如下代碼:
foreach (var entityType in modelBuilder.Model.GetEntityTypes()) {var tableName = entityType.Relational().TableName;modelBuilder.Entity(entityType.Name).ToTable($"{tableName}s"); }同理針對EntityFramework Core 3.x版本修改成如上注意說明,接下來我們再次注釋掉上述驗證時暴露出的DbSet,最后查詢結果如下:
事情還未結束,配置動態加載模型后,由上只是證明關系映射等沒問題,接下來我們如下配置owned Type,我們將看到會拋出異常。
很顯然,雖然我們只是加載了模型,但是對于映射關系通過約定可以得到,而owned Type必須顯式配置,所以在遍歷生成模型時,我們恐怕還需要額外處理owned Type,遺留的這個問題等待空閑時再弄下,暫時就到這里吧。
本節我們詳細講解了在EntityFramework Core如何動態加載模型,同時針對動態加載模型所帶來的問題也只是進行了一丟丟的論述,來,我們下一個結論:在EntityFramework Core中根據約定表名為DbSet屬性名稱,若在上下文中未暴露DbSet屬性,則表名為模型名稱,如果采用動態加載模型,那么表名必須與模型名稱一致,否則將拋出異常,當然我們也可以根據實際項目約定更改表名。通過本節動態加載模型將引入下一節內容:EntityFramework Core表名原理解析,感謝您的閱讀,下一節內容相信很快就會到來。
總結
以上是生活随笔為你收集整理的EntityFramework Core动态加载模型,我们要知道些什么呢?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core Web程序托管
- 下一篇: 从零开始开发 VS Code 插件之 T