目录
1.准备工作
1.1员工类
假设你有一个员工类Employee,如下:
public class Employee
{
public int EmployeeID { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int? Salary { get; set; }
public string Dept { get; set; }
}
1.2 安装EFcore包以及mysql包
并想固化这个员工数据,通常用EFcore是最自然的想法,直接引入三个nuget包:
这里打算使用本地的mysql数据库。
1.3 配置mysql数据库
将数据库连接信息写到appsetting文件中:(注意:根据自己的数据库配置填写ConnectionStrings)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": { "MysqlConnection": "server=localhost; port=3306;database=EFStudy;user=root;password=123456;Connect Timeout=300" }
}
有了数据库配置就可以添加数据库服务,但是因为我们使用的是EFcore,需要添加数据库上下文环境,也就是DbContex,因此,首先要构建这样一个类:
public class EmployeeDbContext:DbContext
{
public DbSet<Employee> Employees { get; set; }
public EmployeeDbContext(DbContextOptions<EmployeeDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//待编辑
}
}
这样我们就可以为这个数据库环境添加服务:
var connectionString= builder.Configuration.GetConnectionString("MysqlConnection");
builder.Services.AddDbContext<EmployeeDbContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))
);
分别使用:Add-Migration 和Update-Migration命令,创建映射,和创建数据库:
(https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=vs忘记了的参考官方页面)
如果成功的话,打开你的数据库,就可以看到新建的表:
空空如也。所以问题来了,可能一个公司一开始就是有员工的,你当然可以后续一个一个添加,也可以也sql脚本批量添加,这里用代码的办法来添加,所以终于要引出本文的主题了。
2.配置种子数据
2.1.简单办法
前面在编写EmployeeDbContext的时候,有个函数是空的,没错,就是下面这个函数:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
我们可以直接在这个函数中添加种子数据,办法很简单:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>().HasData(
new Employee() { Name = "Pranaya", Gender = "Male", Salary = 10000, Dept = "IT" ,EmployeeID=1},
new Employee() { Name = "张三", Gender = "Male", Salary = 100, Dept = "Saler",EmployeeID=2 },
new Employee() { Name = "东方不败", Gender = "Unknow", Salary = 100000, Dept = "Leader",EmployeeID=3 });
}
添加了数据要重新迁移,以及更新数据库:
Now we can create a new migration:
PM> Add-Migration SeedInitialData
And apply it:
PM> Update-Database
现在去数据库看,就有数据了:
2.2 保持干净
如果种子数据比较多,那么势必为塞满当前EmployeeDbContext,既不美观,也不符合Code Clean原则:
我们可以使用扩展函数:
namespace RepositoryPatternStudy.Extensions
{
public static class EFExtension
{
public static void AddSeed(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>().HasData(
new Employee() { Name = "Pranaya", Gender = "Male", Salary = 10000, Dept = "IT", EmployeeID = 1 },
new Employee() { Name = "张三", Gender = "Male", Salary = 100, Dept = "Saler", EmployeeID = 2 },
new Employee() { Name = "东方不败", Gender = "Unknow", Salary = 100000, Dept = "Leader", EmployeeID = 3 }
);
}
}
}
那么原函数就可以写成一行:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddSeed();
//modelBuilder.Entity<Employee>().HasData(
// new Employee() { Name = "Pranaya", Gender = "Male", Salary = 10000, Dept = "IT" ,EmployeeID=1},
// new Employee() { Name = "张三", Gender = "Male", Salary = 100, Dept = "Saler",EmployeeID=2 },
// new Employee() { Name = "东方不败", Gender = "Unknow", Salary = 100000, Dept = "Leader",EmployeeID=3 });
}
重新迁移更新一遍,你会发现效果一样。
2.3 高级一点的做法
如果我们的项目有很多表也有很多初始数据,那么我们的表将变得难以阅读和维护。EFcore提供了一个接口:IEntityTypeConfiguration<T>,使用这个接口,我们可以为不同的实体编写单独的配置类。具体操作如下:
构建一个派生与此接口的配置类:
public class EmployeeConfiguration : IEntityTypeConfiguration<Employee>
{
public void Configure(EntityTypeBuilder<Employee> builder)
{
builder.ToTable("NewEmployee");
builder.Property(s => s.Name)
.IsRequired(false);
builder.Property(s => s.Name)
.HasDefaultValue(true);
builder.HasData(
new Employee() { Name = "Pranaya", Gender = "Male", Salary = 10000, Dept = "IT", EmployeeID = 1 },
new Employee() { Name = "张三", Gender = "Male", Salary = 100, Dept = "Saler", EmployeeID = 2 },
new Employee() { Name = "东方不败", Gender = "Unknow", Salary = 100000, Dept = "Leader", EmployeeID = 3 }
);
}
}
实际上在Configure中不仅可以配置种子数据,还是配置其它内容:
有了这个配置类之后,我们就可以家在到OnModelCreating类中:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new EmployeeConfiguration());
// modelBuilder.AddSeed();
//modelBuilder.Entity<Employee>().HasData(
// new Employee() { Name = "Pranaya", Gender = "Male", Salary = 10000, Dept = "IT" ,EmployeeID=1},
// new Employee() { Name = "张三", Gender = "Male", Salary = 100, Dept = "Saler",EmployeeID=2 },
// new Employee() { Name = "东方不败", Gender = "Unknow", Salary = 100000, Dept = "Leader",EmployeeID=3 });
}
重新,迁移一遍,更新后,你会发现你的表名变为:NewEmployee,且数据都在。
2.4 为了部署,自动迁移
到此为止,种子数据的设置就告一段落了,但是有一个实际问题需要解决,如果我们的服务要部署到服务器,我们可能首先要在服务器手动进行迁移,也就是运行前面两条命令。当每隔服务都有数据库时,这会显得很繁琐,所以我们希望能够在运行时,自动迁移。
首先我们创建一个静态类,因为我们要添加扩展方法:
public static class MigrationManager
{
}
我们往刚才的类加静态方法:
public static IHost MigrationDatabase(this IHost host)
{
using var scope = host.Services.CreateScope();
var service = scope.ServiceProvider;
using var appContext = scope.ServiceProvider.GetRequiredService<EmployeeDbContext>();
try
{
appContext.Database.Migrate();
}
catch (Exception ex)
{
var logger = service.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occured seeding the DB");
}
return host;
}
然后在配置中使用:
var app = builder.Build();
app.MigrationDatabase();
现在,你删掉数据库中的 _EFMigrationsHistory 以及Employee表,重新运行项目,你会发现之前的配置,会自动迁移,种子数据也在表里了。
数据库的配置弄完之后,接下来就是数据仓储了。