由於Scaffold-DbContext指令目前還不支持自動映射數據庫中的視圖為實體,所以當我們想使用EF Core來讀取數據庫視圖數據的時候,我們需要手動去做映射,本文介紹如何在EF Core中手動映射數據庫的視圖為實體。
假設我們在SQL Server中有如下數據庫視圖[dbo].[V_Person]:
CREATE VIEW [dbo].[V_Person] AS SELECT ID, Code, Name, CreateTime, UpdateTime FROM dbo.Person GO
其結構如下,共有五列數據:
在EF Core中映射數據庫視圖有一個硬條件,那就是視圖中必須有一列要擁有唯一值,而如果你的視圖中沒有唯一列請在視圖中構造一個唯一列(可以參考這個鏈接的文章)。這是因為EF Core要求每一個實體要擁有唯一屬性(Key),而既然我們要把數據庫視圖映射為EF Core的實體,那么視圖就要求必須有一個唯一列。
本例中數據庫視圖V_Person的列ID是唯一的。
首先我們定義一個實體類V_Person,用來映射數據庫視圖V_Person,其屬性和數據庫視圖V_Person的列一一對應:
using System; namespace FFCoreView.Entities { /// <summary> /// 定義實體V_Person,其結構和數據庫視圖V_Person相同 /// </summary> public class V_Person { public int Id { get; set; } public string Code { get; set; } public string Name { get; set; } public DateTime? CreateTime { get; set; } public DateTime? UpdateTime { get; set; } } }
然后我們來構造一個自定義的DbContext類TestDbExContext,其繼承於Scaffold-DbContext指令自動生成的DbContext類TestDBContext:
using FFCoreView.Entities; using Microsoft.EntityFrameworkCore; namespace FFCoreView.Extension { /// <summary> /// TestDbExContext繼承TestDBContext,而TestDBContext又繼承DbContext /// </summary> public class TestDbExContext : TestDBContext { /// <summary> /// 定義一個DbSet<V_Person>的集合屬性V_Person,EF Core會自動為其賦值,然后可以利用TestDbExContext.V_Person屬性來讀取數據庫中V_Person視圖的數據 /// </summary> public virtual DbSet<V_Person> V_Person { get; set; } /// <summary> /// 在重寫的OnModelCreating方法中,使用Fluent API來設置實體V_Person和數據庫中V_Person視圖的關系 /// </summary> protected override void OnModelCreating(ModelBuilder modelBuilder) { //先調用基類的OnModelCreating方法,設置數據庫中其它表和實體的映射關系 base.OnModelCreating(modelBuilder); //接着設置實體V_Person和數據庫中V_Person視圖的關系 modelBuilder.Entity<V_Person>(entity => { //告訴EF Core實體V_Person對應數據庫中的V_Person視圖,這里使用entity.ToTable方法后,上面的DbSet<V_Person> V_Person集合屬性可以叫任何名字,比如我們可以將其定義為DbSet<V_Person> V_People也可以,如果不使用entity.ToTable方法,那么DbSet<V_Person> V_Person的屬性名字必須和數據庫視圖V_Person的名字相同,否則EF Core會報錯 entity.ToTable("V_Person"); //設置實體的唯一屬性,因為我們知道數據庫中V_Person視圖的ID列值是唯一的,所以這里我們設置實體V_Person的Id屬性為唯一屬性 entity.HasKey(e => e.Id); //利用Fluent API將實體V_Person的每一列映射到數據庫視圖的每一列 entity.Property(e => e.Id).HasColumnName("ID"); entity.Property(e => e.Code).HasColumnName("Code"); entity.Property(e => e.Name).HasColumnName("Name"); entity.Property(e => e.CreateTime).HasColumnName("CreateTime"); entity.Property(e => e.UpdateTime).HasColumnName("UpdateTime"); }); } } }
在我們自定義的TestDbExContext類中,我們定義了個DbSet<V_Person> V_Person集合屬性,EF Core會為我們自動為其賦值,我們可以使用這個集合屬性來讀取數據庫視圖V_Person的所有數據,然后重寫了DbContext的OnModelCreating方法,使用Fluent API來設置了實體V_Person和數據庫中V_Person視圖的關系。
然后我們在.NET Core控制台項目的Program類Main方法中,來使用自定義的TestDbExContext類讀取視圖V_Person的數據,注意加上AsNoTracking方法可以提高EF Core讀取數據庫視圖數據的效率, 因為我們不會用實體來更改數據庫視圖的數據,所以可以用AsNoTracking方法來取消DbContext跟蹤實體V_Person:
using FFCoreView.Extension; using Microsoft.EntityFrameworkCore; using System; using System.Linq; namespace FFCoreView { class Program { static void Main(string[] args) { using(TestDbExContext dbContext=new TestDbExContext()) { var vPersons = dbContext.V_Person.AsNoTracking().ToList();//通過TestDbExContext.V_Person屬性從數據庫中查詢視圖數據,因為和數據庫表不同,我們不會更新數據庫視圖的數據,所以調用AsNoTracking方法來告訴EF Core不用在DbContext中跟蹤返回的V_Person實體,可以提高EF Core的運行效率 Console.WriteLine($"V_Person視圖有{vPersons.Count.ToString()}行數據"); } Console.WriteLine("Press any key to quit..."); Console.ReadKey(); } } }
運行結果如下,我們成功讀出了數據庫視圖V_Person的三行數據:
注意:
在EF Core 3.0中Scaffold-DbContext指令已經可以自動映射數據庫中的視圖為實體,所以就不需要采用本文所述的方法了。