Entity Framework应用:使用Code First模式管理视图
一、什么是视图
视图在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