Asp.net Mvc 数据库上下文初始化器

在Asp.net Mvc 和Entity FrameWork程序中,如果数据库不存在,EF默认的行为是新建一个数据库。如果模型类与已有的数据库不匹配的时候,会抛出一个异常。

通过指定数据库上下文对象初始化器,可以执行删除和重新创建数据库,并用种子方法填充数据库。

当Asp.net Mvc 应用程序每一次运行的时候,Entity Framework 能够自动创建(或者删除重新创建)数据库。你能够指定每一次程序运行的时候,或者模型与已有数据库不匹配的时候执行 删除重新创建操作,通过指定Seed 方法,EF能自动的调用Seed方法在你重新创建数据库后自动的填充数据。

注意:在数据迁移中执行种子方法后,会将种子方法中的数据记录还原成种子方法中的记录值,也就是说不能保存在U中I做的的更改。因为每次迁移后执行Update-Database命令更新数据后都要调用种子方法。

而采用初始化器的方式如果模型类没有变化,就不会执行初始化器中的种子方法,所以在UI中更改或删除记录的变化 是保存的。

第一种场景:在开发阶段,模型类需要快速迭代,为了保持生成的数据库与模型类一致,需要删除重新创建数据库,但会导致丢失测试的数据,所以使用EF数据库初始化器并用种子方法自动填充测试数据,以方便测试。

第一步:创建数据库初始化器。

1、创建DropCreateDatabaseAlways<DbContext>数据库初始化器。在 程序每次运行的时候都执行删除、重新创建数据库操作

public class SchoolInitializer:DropCreateDatabaseAlways<SchoolContext>

{

protected override void Seed(SchoolContext context) { var students = new List<Student> { new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")}, new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")}, new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")}, new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")}, new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")}, new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")}, new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")}, new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")} }; students.ForEach(s => context.Students.Add(s)); context.SaveChanges();

2、创建DropCreateDatabaseIfModelChanges<DbContext>数据库初始化器。当模型发生更改的时候,执行删除、重新创建数据库操作

public class SchoolInitializer:DropCreateDatabaseIfModelChanges<SchoolContext>

{

protected override void Seed(SchoolContext context)

{

var students = new List<Student>

{

new Student {FirstMidName ="张",LastName="三",EnrollmentDate=DateTime.Parse("1979-9-1")}

new Student {FirstMidName ="李",LastName="四",EnrollmentDate=DateTime.Parse("2000-9-1")},

new Student {FirstMidName ="王",LastName="二",EnrollmentDate=DateTime.Parse("1979-9-1")}

new Student {FirstMidName ="陈",LastName="五",EnrollmentDate=DateTime.Parse("2000-9-1")},

};

students.ForEach(s => context.Students.Add(s));

context.SaveChanges();

第2步:在应用程序中指定数据库初始化器。有两种方法

1、在Asp.Net Mvc 应用程序 根目录的 Web.Config 的XML文件中的<EntityFramework>节点中配置

<entityFramework>
  <contexts>
    <context type="ContosoUniversity.DAL.SchoolContext, ContosoUniversity">
      <databaseInitializer type="ContosoUniversity.DAL.SchoolInitializer, ContosoUniversity" />   //disableDatabaseInitialization="true".可以关闭初始化器
    </context>
  </contexts>

2、在全局应用程序配置文件Global.asax 中的 Application_Start 方法中通过代码的方式指定。

Database.SetInitializer<SchoolContext>(new SchoolInitializer());

第二种场景:部署阶段 如果需要在部署的产品中存储初始化数据,比如在角色权限系统中的超级管理员用户名、密码信息,以便能够登录,或执行其他一些初始化操作,也需要定义和使用一个 数据库上下文初始化器,只能使用 DropCreateDatabaseIfModelChanges<SchoolContext>

第一步:定义一个数据库初始化器

public class MajorContextInitializer:DropCreateDatabaseIfModelChanges<MajorContext>

{

protected override void Seed(MajorContext context)

{

var themeList = new List<BootstrapTheme>()

{

new BootstrapTheme { BootstrapThemeStock" ,ThemeDescription="默认主题。黑色背景导航条,蓝色背景标题", IsActived=true},

new BootstrapTheme { BootstrapThemeCerulean" ,ThemeDescription="深蓝色背景导航条,浅蓝色背景标题", IsActived=false},

new BootstrapTheme { BootstrapThemeCosmo" ,ThemeDescription="天蓝色背景导航条,天蓝色背景标题", IsActived=false},

new BootstrapTheme { BootstrapThemeDarkly" ,ThemeDescription="翠绿色背景导航条,蓝黑色背景标题,黑色背景", IsActived=false},

new BootstrapTheme { BootstrapThemeFlatly" ,ThemeDescription="翠绿色背景导航条,蓝黑色背景标题", IsActived=false},

new BootstrapTheme { BootstrapThemeSandstone" ,ThemeDescription="绿色背景导航条,蓝黑色背景标题", IsActived=false},

new BootstrapTheme { BootstrapThemeSpacelab" ,ThemeDescription="深蓝色背景导航条,深蓝色背景标题", IsActived=false},

new BootstrapTheme { BootstrapThemeSuperhero" ,ThemeDescription="橘黄色背景导航条,橘黄色背景标题,小号字", IsActived=false},

new BootstrapTheme { BootstrapThemeUnited" ,ThemeDescription="朱红色背景导航条,橘黄色背景标题", IsActived=false},

new BootstrapTheme { BootstrapThemeYeti" ,ThemeDescription="蓝色背景导航条,蓝色背景标题,小号字", IsActived=false}

};

//这里如果不使用Foreach 语句遍历,也可以使用 themeList.Foreach(s =>context.BootstrapThemes.AddOrUpdate(p =>p.ThemeName,s)) 执行upsert操作,以themeName 主题名称作为区分不同的主题。

foreach (var theme in themeList)

{

var _theme = context.BootstrapThemes.Where(x => x.ThemeName == theme.ThemeName).FirstOrDefault();

if (_theme == null)

{

context.BootstrapThemes.Add(theme);

}

}

context.SaveChanges(); //这里实际上并需要使用context.SaveChages().主要是方便调试,定位错误出在哪里。

//既然使用静态构造函数可以初始化主题,那么也应该可以初始化其它DbSet 实体集成员。

var categoryList = new List<Category>()

{

new Category{ Category专业动态",CategoryDescription="专业建设进展情况",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=1},

new Category{ Category运行机制",CategoryDescription="制度建设情况",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=2},

new Category{ Category教学队伍",CategoryDescription="专业带头人、师资队伍、团队建设情况",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=3},

new Category{ Category实践条件",CategoryDescription="校内、校外实训基地建设",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=4},

new Category{ Category培养方案",CategoryDescription="人才培养方案",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=5},

new Category{ Category校企合作",CategoryDescription="校企合作情况",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=6},

new Category{ Category社会服务",CategoryDescription="社会服务情况",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=7},

new Category{ Category政策法规",CategoryDescription="高等职业教育制度",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=8},

new Category{ Category课程体系",CategoryDescription="课程体系构成",NavbarIsShow=true,IndexPageIsShow=true, PriorOrder=9}

};

foreach(var category in categoryList)

{

var _category = context.Categories.Where(x => x.CategoryName == category.CategoryName).FirstOrDefault();

if (_category == null)

{

context.Categories.Add(category);

}

}

context.SaveChanges(); //这里实际上并需要使用context.SaveChages().主要是方便调试,定位错误出在哪里。

//添加一条默认的专业名称记录;

var majorName = new MajorName { MajorNameID = Guid.NewGuid().ToString(), MajorNameText = "数控技术" };

if(context.MajorNames.Count() ==0)

{

context.MajorNames.Add(majorName);

}

context.SaveChanges(); //这里实际上并需要使用context.SaveChages().主要是方便调试,定位错误出在哪里。

base.Seed(context);

}

}

第二步:在数据库上下文对象中中定义一个数据库静态构造函数,在静态构造函数中设定初始化器。则每一次调用数据库上下文对象的时候都要检查数据库中是否存在这些初始值,会降低程序的响应速度。 这种方法不是最佳实践。网站性能并不如在应用程序启动的时候才执行初始化(可在global.asx或在webconfig 中指定DatabaseInitializer指定初始化器)。

public class MajorContext:DbContext

{

public DbSet<Category> Categories { get; set; }

public DbSet<Article> Articles { get; set; }

public DbSet<Course> Courses { get; set; }

public DbSet<Resource> Resources { get; set; }

public DbSet<BootstrapTheme> BootstrapThemes { get; set; }

public DbSet<MajorName> MajorNames { get; set; }

public MajorContext()

: base("MajorContext")

{ }

//静态构造函数。MSDN:静态构造函数用于初始化任何静态数据,或用于执行仅需执行一次的特定操作。在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数

static MajorContext()

{

//设置数据库初始化器,它就在应用程序运行的时候加载。

Database.SetInitializer<MajorContext>(new MajorContextInitializer());

}

protected override void OnModelCreating(DbModelBuilder modelBuilder) // OnModelCreating 方法重写的目的是指定对应Dbse实体集的表格的名称应该是单数,如果没有此方法,数据库中的表格名称会按惯例为复数的名称,如 Studnets、Courses

{

modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

}

}