一、什么是視圖
視圖在RDBMS(關系型數據庫管理系統)中扮演了一個重要的角色,它是將多個表的數據聯結成一種看起來像是一張表的結構,但是沒有提供持久化。因此,可以將視圖看成是一個原生表數據頂層的一個抽象。例如,我們可以使用視圖提供不同安全的級別,也可以簡化必須編寫的查詢,尤其是我們可以在代碼中的多個地方頻繁地訪問使用視圖定義的數據。EF Code First模式現在還不完全支持視圖,因此我們必須使用一種變通的方法。這種方法是:將視圖真正看成是一張表,讓EF定義這張表,然后在刪除它,最后再創建一個代替它的視圖。
二、使用EF的Code First模式管理視圖
以圖書和圖書類型為例講解如何使用EF的Code First模式管理視圖。
1、創建實體類
BookType實體類定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace CodeFirstViewApp.Model 8 { 9 public class BookType 10 { 11 public BookType() 12 { 13 Books = new HashSet<Book>(); 14 } 15 16 public int BookTypeId { get; set; } 17 18 public string BookTypeName { get; set; } 19 20 public virtual ICollection<Book> Books { get; set; } 21 } 22 }
Book實體類定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace CodeFirstViewApp.Model 8 { 9 public class Book 10 { 11 public int Id { get; set; } 12 13 public string Name { get; set; } 14 15 public string Author { get; set; } 16 17 public DateTime PublicationDate { get; set; } 18 19 public virtual BookType BookType { get; set; } 20 } 21 }
2、創建模擬視圖類
從多個實體中取出想要的列組合成一個實體,BookView模擬視圖類定義如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace CodeFirstViewApp.Model 8 { 9 public class BookView 10 { 11 public int BookId { get; set; } 12 13 public string BookName { get; set; } 14 15 public string Author { get; set; } 16 17 public DateTime PublicationDate { get; set; } 18 19 public string BookTypeName { get; set; } 20 } 21 }
3、為模擬視圖類創建配置伙伴類
下面的代碼指定了表名和主鍵。
注意:表名也是視圖的名字,這里的表名一定要和創建視圖的語句中的視圖名一致。
1 using CodeFirstViewApp.Model; 2 using System; 3 using System.Collections.Generic; 4 using System.Data.Entity.ModelConfiguration; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace CodeFirstViewApp.Map 10 { 11 /// <summary> 12 /// 定義配置伙伴類 13 /// </summary> 14 public class BookViewMap : EntityTypeConfiguration<BookView> 15 { 16 public BookViewMap() 17 { 18 // 設置表名 19 this.ToTable("BookViews"); 20 // 設置主鍵 21 HasKey(p => p.BookId); 22 } 23 } 24 }
4、創建種子數據初始化器類
1 using CodeFirstViewApp.Model; 2 using System; 3 using System.Collections.Generic; 4 using System.Data.Entity; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 namespace CodeFirstViewApp.EF 10 { 11 public class Initializer :DropCreateDatabaseAlways<EFDbContext> 12 { 13 /// <summary> 14 /// 重新Seed方法 15 /// </summary> 16 /// <param name="context"></param> 17 protected override void Seed(EFDbContext context) 18 { 19 // 創建初始化數據 20 BookType bookType = new BookType() 21 { 22 BookTypeName = "文學小說", 23 Books = new List<Book> 24 { 25 new Book(){Name="人間失格",Author="太宰治",PublicationDate=DateTime.Parse("2015-08-01")}, 26 new Book(){Name="解憂雜貨店",Author="東野圭吾",PublicationDate=DateTime.Parse("2014-05-01")}, 27 new Book(){Name="追風箏的人",Author="卡勒德胡賽尼",PublicationDate=DateTime.Parse("2006-08-01")}, 28 new Book(){Name="百年孤獨",Author="加西亞馬爾克斯",PublicationDate=DateTime.Parse("2011-06-01")}, 29 new Book(){Name="霍亂時期的愛情",Author="加西亞馬爾克斯",PublicationDate=DateTime.Parse("2015-06-01")} 30 } 31 }; 32 33 BookType bookType2 = new BookType() 34 { 35 BookTypeName = "科學", 36 Books = new List<Book> 37 { 38 new Book(){Name="人類簡史",Author="尤瓦爾赫拉利",PublicationDate=DateTime.Parse("2017-01-01")} 39 } 40 }; 41 42 context.BookTypes.Add(bookType); 43 context.BookTypes.Add(bookType2); 44 45 // 先刪除表 46 var drop = "Drop Table BookViews"; 47 context.Database.ExecuteSqlCommand(drop); 48 49 // 創建視圖 50 var createView = @"CREATE VIEW [dbo].[BookViews] 51 AS SELECT 52 dbo.Books.Id AS BookId, 53 dbo.Books.Name AS BookName, 54 dbo.Books.Author AS Author, 55 dbo.Books.PublicationDate AS PublicationDate, 56 dbo.BookTypes.BookTypeName AS BookTypeName 57 FROM dbo.Books 58 INNER JOIN dbo.BookTypes ON dbo.BookTypes.BookTypeId=dbo.Books.BookTypeId"; 59 context.Database.ExecuteSqlCommand(createView); 60 base.Seed(context); 61 } 62 } 63 }
上面的代碼中,我們先使用Database對象的ExecuteSqlCommand()方法銷毀生成的表,然后又調用該方法創建我們需要的視圖。該方法在允許開發者對后端執行任意的SQL代碼時很有用。
5、創建數據上下文類
把實體類添加到數據上下文中,並配置實體之間的關系
1 using CodeFirstViewApp.Map; 2 using CodeFirstViewApp.Model; 3 using System; 4 using System.Collections.Generic; 5 using System.Data.Entity; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 10 namespace CodeFirstViewApp.EF 11 { 12 public class EFDbContext:DbContext 13 { 14 public EFDbContext() 15 : base("name=AppConnection") 16 { 17 Database.SetInitializer(new Initializer()); 18 } 19 20 // 添加到數據上下文中 21 public DbSet<Book> Books { get; set; } 22 23 public DbSet<BookType> BookTypes { get; set; } 24 25 public DbSet<BookView> BookViews { get; set; } 26 27 protected override void OnModelCreating(DbModelBuilder modelBuilder) 28 { 29 // 配置表名和主鍵 30 modelBuilder.Entity<Book>().ToTable("Books").HasKey(p => p.Id); 31 modelBuilder.Entity<BookType>().ToTable("BookTypes").HasKey(p => p.BookTypeId); 32 // 設置實體關系 33 // BookType和 Books 一對多關系 外鍵:BookTypeId 34 modelBuilder.Entity<BookType>().HasMany(p => p.Books).WithRequired(t => t.BookType) 35 .Map(m => 36 { 37 m.MapKey("BookTypeId"); 38 }); 39 40 // 添加配置伙伴類 41 modelBuilder.Configurations.Add(new BookViewMap()); 42 base.OnModelCreating(modelBuilder); 43 } 44 } 45 }
6、運行程序
Main()方法定義如下:
1 using CodeFirstViewApp.EF; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 8 namespace CodeFirstViewApp 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 using (var context = new EFDbContext()) 15 { 16 // 獲取視圖的數據 17 var bookView = context.BookViews; 18 19 // 循環遍歷 20 bookView.ToList().ForEach(p => 21 { 22 Console.WriteLine("Id:" + p.BookId + ",Name:" + p.BookName + ",BookTypeName;" + p.BookTypeName + ",PublicationDate:" + p.PublicationDate); 23 }); 24 } 25 26 Console.ReadKey(); 27 } 28 } 29 }
運行程序,就會看到數據庫中已經生成了Books和BookTypes兩張表和BookViews視圖,見下圖:
運行結果如下圖:
直接在數據庫中查詢視圖:
注意:訪問視圖和任意數據表在代碼層面沒有任何區別,需要注意的地方就是在Seed()方法中定義的視圖名稱要和定義的表名一致,否則就會因為找不到表對象而報錯。
示例代碼下載地址:https://pan.baidu.com/s/1gf2FzDD