这一节我们说说数据库迁移(Migration)。
我们之前用的DBFirst创建了实体类,但当初这么做的原因是为了节省时间。现在我们通过创建的实体类和DbContext类利用EF的Code First数据库迁移反过来创建数据库。ABP模板默认开启了迁移,并且添加了一下下面的Configuration类:
1 using EntityFramework.DynamicFilters; 2 using System.Data.Entity.Migrations; 3 using Witcare.PSS.Migrations.SeedData; 4 5 namespace Witcare.PSS.Migrations 6 { 7 internal sealed class Configuration : DbMigrationsConfiguration<PSS.EntityFramework.PSSDbContext> 8 { 9 public Configuration() 10 { 11 AutomaticMigrationsEnabled = false; 12 ContextKey = "PSS"; 13 } 14 /// <summary> 15 /// 添加种子数据,比如默认管理员等数据 16 /// </summary> 17 /// <param name="context">当前数据库上下文子类</param> 18 protected override void Seed(PSS.EntityFramework.PSSDbContext context) 19 { 20 context.DisableAllFilters(); 21 new InitialDataBuilder(context).Build(); 22 } 23 } 24 }
1 using System.Linq; 2 using Abp.Authorization; 3 using Abp.Authorization.Roles; 4 using Abp.Authorization.Users; 5 using Abp.MultiTenancy; 6 using Witcare.PSS.Authorization; 7 using Witcare.PSS.Authorization.Roles; 8 using Witcare.PSS.EntityFramework; 9 using Witcare.PSS.MultiTenancy; 10 using Witcare.PSS.Users; 11 using Microsoft.AspNet.Identity; 12 13 namespace Witcare.PSS.Migrations.SeedData 14 { 15 public class DefaultTenantRoleAndUserBuilder 16 { 17 private readonly PSSDbContext _context; 18 19 public DefaultTenantRoleAndUserBuilder(PSSDbContext context) 20 { 21 _context = context; 22 } 23 24 public void Build() 25 { 26 CreateUserAndRoles(); 27 } 28 29 private void CreateUserAndRoles() 30 { 31 //Admin role for host 32 33 var adminRoleForHost = _context.Roles.FirstOrDefault(r => r.TenantId == null && r.Name == StaticRoleNames.Host.Admin); 34 if (adminRoleForHost == null) 35 { 36 adminRoleForHost = _context.Roles.Add(new Role { Name = StaticRoleNames.Host.Admin, DisplayName = StaticRoleNames.Host.Admin, IsStatic = true }); 37 _context.SaveChanges(); 38 39 //Grant all tenant permissions 40 var permissions = PermissionFinder 41 .GetAllPermissions(new PSSAuthorizationProvider()) 42 .Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Host)) 43 .ToList(); 44 45 foreach (var permission in permissions) 46 { 47 if (!permission.IsGrantedByDefault) 48 { 49 _context.Permissions.Add( 50 new RolePermissionSetting 51 { 52 Name = permission.Name, 53 IsGranted = true, 54 RoleId = adminRoleForHost.Id 55 }); 56 } 57 } 58 59 _context.SaveChanges(); 60 } 61 62 //Admin user for tenancy host 63 64 var adminUserForHost = _context.Users.FirstOrDefault(u => u.TenantId == null && u.UserName == User.AdminUserName); 65 if (adminUserForHost == null) 66 { 67 adminUserForHost = _context.Users.Add( 68 new User 69 { 70 TenantId = null, 71 UserName = User.AdminUserName, 72 Name = "System", 73 Surname = "Administrator", 74 EmailAddress = "admin@aspnetboilerplate.com", 75 IsEmailConfirmed = true, 76 Password = new PasswordHasher().HashPassword(User.DefaultPassword) 77 }); 78 79 _context.SaveChanges(); 80 81 _context.UserRoles.Add(new UserRole(adminUserForHost.Id, adminRoleForHost.Id)); 82 83 _context.SaveChanges(); 84 } 85 86 //Default tenant 87 88 var defaultTenant = _context.Tenants.FirstOrDefault(t => t.TenancyName == "Default"); 89 if (defaultTenant == null) 90 { 91 defaultTenant = _context.Tenants.Add(new Tenant { TenancyName = "Default", Name = "Default" }); 92 _context.SaveChanges(); 93 } 94 95 //Admin role for 'Default' tenant 96 97 var adminRoleForDefaultTenant = _context.Roles.FirstOrDefault(r => r.TenantId == defaultTenant.Id && r.Name == StaticRoleNames.Tenants.Admin); 98 if (adminRoleForDefaultTenant == null) 99 { 100 adminRoleForDefaultTenant = _context.Roles.Add(new Role { TenantId = defaultTenant.Id, Name = StaticRoleNames.Tenants.Admin, DisplayName = StaticRoleNames.Tenants.Admin, IsStatic = true }); 101 _context.SaveChanges(); 102 103 //Grant all tenant permissions 104 var permissions = PermissionFinder 105 .GetAllPermissions(new PSSAuthorizationProvider()) 106 .Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Tenant)) 107 .ToList(); 108 109 foreach (var permission in permissions) 110 { 111 if (!permission.IsGrantedByDefault) 112 { 113 _context.Permissions.Add( 114 new RolePermissionSetting 115 { 116 Name = permission.Name, 117 IsGranted = true, 118 RoleId = adminRoleForDefaultTenant.Id 119 }); 120 } 121 } 122 123 _context.SaveChanges(); 124 } 125 126 //Admin for 'Default' tenant 127 128 var adminUserForDefaultTenant = _context.Users.FirstOrDefault(u => u.TenantId == defaultTenant.Id && u.UserName == User.AdminUserName); 129 if (adminUserForDefaultTenant == null) 130 { 131 adminUserForDefaultTenant = _context.Users.Add( 132 new User 133 { 134 TenantId = defaultTenant.Id, 135 UserName = User.AdminUserName, 136 Name = "System", 137 Surname = "Administrator", 138 EmailAddress = "admin@aspnetboilerplate.com", 139 IsEmailConfirmed = true, 140 Password = new PasswordHasher().HashPassword(User.DefaultPassword) 141 }); 142 _context.SaveChanges(); 143 144 _context.UserRoles.Add(new UserRole(adminUserForDefaultTenant.Id, adminRoleForDefaultTenant.Id)); 145 _context.SaveChanges(); 146 } 147 } 148 } 149 }
在Seed方法中,添加了租户,角色和用户数据。现在,我来创建初始化迁移。打开包管理器控制台,输入下面的命令:
此处特别注意,红色方框中一定不要忘了选择EF项目,否则不会有下面出现的命令“Add-Migration”,”InitialData”是生成文件的后缀名(也是文件中类的名字),也可以取其他名字。
可以看到生成的文件一个以cs结尾,这里面的代码是创建数据库中表的,另一个以Designer.cs结尾,记录的是数据库迁移的版本记录,最后一个以.resx文件是资源文件,暂且不需要考虑。
刚才我们只是创建了创建数据库所需要的类,但还没有创建数据库。为了创建数据库,需要在包管理控制台执行以下命令:
PM> Update-Database 这边要记得:一定要把web设为启动项目
该命令完成了这次数据库的迁移,创建了数据库并填充了种子数据。
当我们改变实体类时,可以使用Add-Migration命令创建新的迁移类和Update-Database命令更新数据库。
至此,数据库迁移完成。下一次我们说说《定义仓储》。