使用EF.Core将同一模型映射到多个表
在 EntityFramework Core 中,我們可以使用屬性或Fluent API來(lái)配置模型映射。有一天,我遇到了一個(gè)新的需求,有一個(gè)系統(tǒng)每天會(huì)生成大量數(shù)據(jù),每天生成一個(gè)新的表存儲(chǔ)數(shù)據(jù)。例如,數(shù)據(jù)庫(kù)如下所示:
所有表都具有相同的結(jié)構(gòu)。那么,如何更改映射以避免創(chuàng)建多個(gè)模型呢?
在本文中,我將向您展示如何更改映射以處理這種情況。您也可以使用此方法擴(kuò)展出更多的用法。
創(chuàng)建 .NET Core 3.1 項(xiàng)目
現(xiàn)在,我們可以使用.NET Core 3.1,它是.NET Core的LTS版本,將來(lái)可以輕松將其升級(jí)到.NET 5。
假設(shè)您已經(jīng)在計(jì)算機(jī)上安裝了最新的.NET Core SDK。如果沒(méi)有,則可以從https://dotnet.microsoft.com/download下載。然后,您可以使用dotnet CLI創(chuàng)建項(xiàng)目。對(duì)于此示例,我將使用.NET Core 3.1。
讓我們創(chuàng)建一個(gè)名為DynamicModelDemo的新.NET Core Console項(xiàng)目:
dotnet new console --name DynamicModelDemo然后用以下命令創(chuàng)建一個(gè)新的解決方案:
dotnet new sln --name DynamicModelDemo接下來(lái)使用以下命令把剛才創(chuàng)建的項(xiàng)目添加到解決方案:
dotnet sln add "DynamicModelDemo/DynamicModelDemo.csproj"接下來(lái)可以用Visual Studio打開(kāi)解決方案了。
創(chuàng)建模型
該模型非常簡(jiǎn)單。在項(xiàng)目中添加一個(gè)名為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屬性來(lái)確定模型應(yīng)該映射到哪個(gè)表。
添加 EntityFramework Core
導(dǎo)航到項(xiàng)目目錄并使用以下命令添加所需的EF.Core packages:
dotnet add package Microsoft.EntityFrameworkCore.SqlSever dotnet add package Microsoft.EntityFrameworkCore.Design如果您還沒(méi)有安裝 ef tool,請(qǐng)運(yùn)行以下命令來(lái)安裝:
dotnet tool install --global dotnet-ef這樣您就可以使用 dotnet ef 工具創(chuàng)建遷移或通過(guò)應(yīng)用遷移來(lái)更新數(shù)據(jù)庫(kù)。
創(chuàng)建 DbContext
向項(xiàng)目添加一個(gè)名為DynamicContext.cs的新類(lèi)文件。內(nèi)容如下所示:
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的基本配置。它使用默認(rèn)映射,這意味著模型將映射到名為Entities的表。那么,如果我們想基于其CreateDateTime屬性將模型映射到不同的表,該怎么辦呢?
您可能知道我們可以使用ToTable()方法來(lái)更改表名,但是如何在OnModelCreating方法中更改所有模型的表名呢?EF建立模型時(shí),只會(huì)執(zhí)行一次OnModelCreating。所以這種方式是無(wú)法實(shí)現(xiàn)的。
對(duì)于這種情況,我們需要使用IModelCacheKeyFactory來(lái)更改默認(rèn)映射,通過(guò)這個(gè)接口我們可以定制模型緩存機(jī)制,以便EF能夠根據(jù)其屬性創(chuàng)建不同的模型。
IModelCacheKeyFactory是什么?
這是微軟官方的文檔解釋:
EF uses IModelCacheKeyFactory to generate cache keys for models.
默認(rèn)情況下,EF假定對(duì)于任何給定的上下文類(lèi)型,模型都是相同的。但是對(duì)于我們的方案,模型將有所不同,因?yàn)樗成涞搅瞬煌谋怼R虼?#xff0c;我們需要用我們的實(shí)現(xiàn)替換IModelCacheKeyFactory服務(wù),該實(shí)現(xiàn)會(huì)比較緩存鍵以將模型映射到正確的表。
請(qǐng)注意,該接口通常由數(shù)據(jù)庫(kù)提供程序和其他擴(kuò)展使用,一般不在應(yīng)用程序代碼中使用。但是對(duì)于我們的場(chǎng)景來(lái)說(shuō),這是一種可行的方法。
實(shí)現(xiàn)IModelCacheKeyFactory
我們需要使用CreateDateTime來(lái)區(qū)分表。在DynamicContext類(lèi)中添加一個(gè)屬性:
public DateTime CreateDateTime { get; set; }在項(xiàng)目中添加一個(gè)名為DynamicModelCacheKeyFactory.cs的新類(lèi)文件。代碼如下所示:
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();} }在生成模型緩存鍵時(shí),此實(shí)現(xiàn)將考慮CreateDateTime屬性。
應(yīng)用IModelCacheKeyFactory
接下來(lái),我們可以在上下文中注冊(cè)新的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來(lái)自DynamicContext的屬性。
我們可以在創(chuàng)建DynamicContext時(shí)指定CreateDateTime屬性:
var context = new DynamicContext { CreateDateTime = datetime };如果datetime為2020/03/27,則context的模型將映射到名為20200327的表。
創(chuàng)建數(shù)據(jù)庫(kù)
在驗(yàn)證代碼之前,我們需要首先創(chuàng)建數(shù)據(jù)庫(kù)。但是,EF遷移并不是這種情況的最佳解決方案,因?yàn)殡S著時(shí)間的流逝,系統(tǒng)將生成更多表。我們只是使用它來(lái)創(chuàng)建一些示例表來(lái)驗(yàn)證映射。實(shí)際上,系統(tǒng)應(yīng)該具有另一種每天動(dòng)態(tài)生成表的方式。
運(yùn)行以下命令以創(chuàng)建第一個(gè)遷移:
dotnet ef migrations add InitialCreate您會(huì)看到在Migrations文件夾中生成了兩個(gè)文件。打開(kāi)xxx_InitialCreate.cs文件,并通過(guò)以下代碼更新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);});}}所做的更改是為了確保數(shù)據(jù)庫(kù)中可以有足夠的表進(jìn)行測(cè)試。請(qǐng)注意,我們不應(yīng)該在生產(chǎn)環(huán)境中使用這種方式。
接下來(lái),我們可以使用此命令來(lái)創(chuàng)建和更新數(shù)據(jù)庫(kù):
dotnet ef database update您會(huì)看到它在數(shù)據(jù)庫(kù)中生成了最近30天的表。
驗(yàn)證映射
現(xiàn)在該驗(yàn)證新映射了。通過(guò)以下代碼更新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}");} }您將會(huì)看到如下輸出:
現(xiàn)在,我們可以通過(guò)傳遞CreateDateTime屬性來(lái)使用相同的DbContext來(lái)表示不同的模型了。
小結(jié)
該演示旨在演示如何使用IModelCacheKeyFactory更改默認(rèn)模型映射。請(qǐng)注意,您仍然需要實(shí)現(xiàn)分別生成表的方法。托管服務(wù)是一種實(shí)現(xiàn)方式。有關(guān)更多信息,請(qǐng)?jiān)L問(wèn)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
求贊賞
????????????
推薦閱讀
【手把手教程】如何讓你的求職簡(jiǎn)歷敲開(kāi)新西蘭雇主的大門(mén)(文末送福利)
【手把手教程】新西蘭求職,如何寫(xiě)好Cover Letter?
再不拼老命我們就真老了——大齡碼農(nóng)DIY新西蘭技術(shù)移民全記錄
移民路上為什么別人總能得到更多的信息,今天知道真相還不算太晚
還在愁紐村的面試嗎?安啦~史上最靠譜的面試題借你看兩眼!
雅思之路——只有絕境沒(méi)有捷徑
身在中國(guó),如何應(yīng)對(duì)海外公司的電話面試?
大齡碼農(nóng)來(lái)新西蘭三個(gè)月拿到兩個(gè)offer,真的只是運(yùn)氣好?
找工作的本命年——從國(guó)內(nèi)大廠到NZ大廠
苦中有甜、笑中含淚的新移民生活——多的是你不知道的事
總結(jié)
以上是生活随笔為你收集整理的使用EF.Core将同一模型映射到多个表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ASP.NET Core分布式项目实战(
- 下一篇: EntityFramework Core