使用EF.Core将同一模型映射到多个表
在 EntityFramework Core 中,我們可以使用屬性或Fluent API來配置模型映射。有一天,我遇到了一個新的需求,有一個系統每天會生成大量數據,每天生成一個新的表存儲數據。例如,數據庫如下所示:
所有表都具有相同的結構。那么,如何更改映射以避免創建多個模型呢?
在本文中,我將向您展示如何更改映射以處理這種情況。您也可以使用此方法擴展出更多的用法。
創建 .NET Core 3.1 項目
現在,我們可以使用.NET Core 3.1,它是.NET Core的LTS版本,將來可以輕松將其升級到.NET 5。
假設您已經在計算機上安裝了最新的.NET Core SDK。如果沒有,則可以從https://dotnet.microsoft.com/download下載。然后,您可以使用dotnet CLI創建項目。對于此示例,我將使用.NET Core 3.1。
讓我們創建一個名為DynamicModelDemo的新.NET Core Console項目:
dotnet new console --name DynamicModelDemo然后用以下命令創建一個新的解決方案:
dotnet new sln --name DynamicModelDemo接下來使用以下命令把剛才創建的項目添加到解決方案:
dotnet sln add "DynamicModelDemo/DynamicModelDemo.csproj"接下來可以用Visual Studio打開解決方案了。
創建模型
該模型非常簡單。在項目中添加一個名為ConfigurableEntity.cs的新文件:
using System;namespace DynamicModelDemo {public class ConfigurableEntity{public int Id { get; set; }public string Title { get; set; }public string Content { get; set; }public DateTime CreateDateTime { get; set; }} }我們將使用CreateDateTime屬性來確定模型應該映射到哪個表。
添加 EntityFramework Core
導航到項目目錄并使用以下命令添加所需的EF.Core packages:
dotnet add package Microsoft.EntityFrameworkCore.SqlSever dotnet add package Microsoft.EntityFrameworkCore.Design如果您還沒有安裝 ef tool,請運行以下命令來安裝:
dotnet tool install --global dotnet-ef這樣您就可以使用 dotnet ef 工具創建遷移或通過應用遷移來更新數據庫。
創建 DbContext
向項目添加一個名為DynamicContext.cs的新類文件。內容如下所示:
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using System;namespace DynamicModelDemo {public class DynamicContext : DbContext{public DbSet<ConfigurableEntity> Entities { get; set; }#region OnConfiguringprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)=> optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=DynamicContext;Trusted_Connection=True;");#endregion#region OnModelCreatingprotected override void OnModelCreating(ModelBuilder modelBuilder){modelBuilder.Entity<ConfigurableEntity>(b =>{b.HasKey(p => p.Id);});}#endregion} }目前,這只是EF.Core的基本配置。它使用默認映射,這意味著模型將映射到名為Entities的表。那么,如果我們想基于其CreateDateTime屬性將模型映射到不同的表,該怎么辦呢?
您可能知道我們可以使用ToTable()方法來更改表名,但是如何在OnModelCreating方法中更改所有模型的表名呢?EF建立模型時,只會執行一次OnModelCreating。所以這種方式是無法實現的。
對于這種情況,我們需要使用IModelCacheKeyFactory來更改默認映射,通過這個接口我們可以定制模型緩存機制,以便EF能夠根據其屬性創建不同的模型。
IModelCacheKeyFactory是什么?
這是微軟官方的文檔解釋:
EF uses IModelCacheKeyFactory to generate cache keys for models.
默認情況下,EF假定對于任何給定的上下文類型,模型都是相同的。但是對于我們的方案,模型將有所不同,因為它映射到了不同的表。因此,我們需要用我們的實現替換IModelCacheKeyFactory服務,該實現會比較緩存鍵以將模型映射到正確的表。
請注意,該接口通常由數據庫提供程序和其他擴展使用,一般不在應用程序代碼中使用。但是對于我們的場景來說,這是一種可行的方法。
實現IModelCacheKeyFactory
我們需要使用CreateDateTime來區分表。在DynamicContext類中添加一個屬性:
public DateTime CreateDateTime { get; set; }在項目中添加一個名為DynamicModelCacheKeyFactory.cs的新類文件。代碼如下所示:
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure;namespace DynamicModelDemo {public class DynamicModelCacheKeyFactory : IModelCacheKeyFactory{public object Create(DbContext context)=> context is DynamicContext dynamicContext? (context.GetType(), dynamicContext.CreateDateTime): (object)context.GetType();} }在生成模型緩存鍵時,此實現將考慮CreateDateTime屬性。
應用IModelCacheKeyFactory
接下來,我們可以在上下文中注冊新的IModelCacheKeyFactory:
#region OnConfiguring protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)=> optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=DynamicContext;Trusted_Connection=True;").ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactory>(); #endregion這樣我們就可以在OnModelCreating方法中分別映射表名了:
#region OnModelCreating protected override void OnModelCreating(ModelBuilder modelBuilder) {modelBuilder.Entity<ConfigurableEntity>(b =>{b.ToTable(CreateDateTime.ToString("yyyyMMdd"));b.HasKey(p => p.Id);}); } #endregionCreateDateTime來自DynamicContext的屬性。
我們可以在創建DynamicContext時指定CreateDateTime屬性:
var context = new DynamicContext { CreateDateTime = datetime };如果datetime為2020/03/27,則context的模型將映射到名為20200327的表。
創建數據庫
在驗證代碼之前,我們需要首先創建數據庫。但是,EF遷移并不是這種情況的最佳解決方案,因為隨著時間的流逝,系統將生成更多表。我們只是使用它來創建一些示例表來驗證映射。實際上,系統應該具有另一種每天動態生成表的方式。
運行以下命令以創建第一個遷移:
dotnet ef migrations add InitialCreate您會看到在Migrations文件夾中生成了兩個文件。打開xxx_InitialCreate.cs文件,并通過以下代碼更新Up方法:
protected override void Up(MigrationBuilder migrationBuilder) {for (int i = 0; i < 30; i++){var index = i;migrationBuilder.CreateTable(name: DateTime.Now.AddDays(-index).ToString("yyyyMMdd"),columns: table => new{Id = table.Column<int>(nullable: false).Annotation("SqlServer:Identity", "1, 1"),Title = table.Column<string>(nullable: true),Content = table.Column<string>(nullable: true),CreateDateTime = table.Column<DateTime>(nullable: false)},constraints: table =>{table.PrimaryKey($"PK_{DateTime.Now.AddDays(-index):yyyyMMdd}", x => x.Id);});}}所做的更改是為了確保數據庫中可以有足夠的表進行測試。請注意,我們不應該在生產環境中使用這種方式。
接下來,我們可以使用此命令來創建和更新數據庫:
dotnet ef database update您會看到它在數據庫中生成了最近30天的表。
驗證映射
現在該驗證新映射了。通過以下代碼更新Program.cs中的Main方法:
static void Main(string[] args) {DateTime datetime1 = DateTime.Now;using (var context = new DynamicContext { CreateDateTime = datetime1 }){context.Entities.Add(new ConfigurableEntity { Title = "Great News One", Content = $"Hello World! I am the news of {datetime1}", CreateDateTime = datetime1 });context.SaveChanges();}DateTime datetime2 = DateTime.Now.AddDays(-1);using (var context = new DynamicContext { CreateDateTime = datetime2 }){context.Entities.Add(new ConfigurableEntity { Title = "Great News Two", Content = $"Hello World! I am the news of {datetime2}", CreateDateTime = datetime2 });context.SaveChanges();}using (var context = new DynamicContext { CreateDateTime = datetime1 }){var entity = context.Entities.Single();// Writes news of todayConsole.WriteLine($"{entity.Title} {entity.Content} {entity.CreateDateTime}");}using (var context = new DynamicContext { CreateDateTime = datetime2 }){var entity = context.Entities.Single();// Writes news of yesterdayConsole.WriteLine($"{entity.Title} {entity.Content} {entity.CreateDateTime}");} }您將會看到如下輸出:
現在,我們可以通過傳遞CreateDateTime屬性來使用相同的DbContext來表示不同的模型了。
小結
該演示旨在演示如何使用IModelCacheKeyFactory更改默認模型映射。請注意,您仍然需要實現分別生成表的方法。托管服務是一種實現方式。有關更多信息,請訪問Background tasks in ASP.NET Core[1]。
參考資料
[1]
Background tasks in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio
求贊賞
????????????
推薦閱讀
【手把手教程】如何讓你的求職簡歷敲開新西蘭雇主的大門(文末送福利)
【手把手教程】新西蘭求職,如何寫好Cover Letter?
再不拼老命我們就真老了——大齡碼農DIY新西蘭技術移民全記錄
移民路上為什么別人總能得到更多的信息,今天知道真相還不算太晚
還在愁紐村的面試嗎?安啦~史上最靠譜的面試題借你看兩眼!
雅思之路——只有絕境沒有捷徑
身在中國,如何應對海外公司的電話面試?
大齡碼農來新西蘭三個月拿到兩個offer,真的只是運氣好?
找工作的本命年——從國內大廠到NZ大廠
苦中有甜、笑中含淚的新移民生活——多的是你不知道的事
總結
以上是生活随笔為你收集整理的使用EF.Core将同一模型映射到多个表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core分布式项目实战(
- 下一篇: EntityFramework Core