系列导航地址http://www.cnblogs.com/fzrain/p/3490137.htmlhtml
已经决定使用EF CodeFirst来建立数据库了,所以咱们使用POCO类(“Plain Old CLR Objects)来定义咱们的Model。咱们经过写标准的.NET类来定义适合咱们API的领域模型。那些POCO类就会为咱们建立数据库。数据库
咱们的培训系统数据库比较简单,首先咱们须要有学生”Students”,导师”Tutors”,除此以外咱们还须要定义课程”Courses”以及科目”Subjects”。在咱们的系统中,咱们容许每一个学生去报名参加不一样的课程app
下图是咱们数据库创建后的结果,我在这里列出来的目的是为了帮助你们更好的理解接下来建立的POCO类(图不是很清晰,凑合看吧):ide
打开VS,建立一个空的类库项目函数
.NET Framewok版本的话我用的事4.5的,选4.0的也行性能
右击引用->管理NuGet程序包->右上角输入Entity Framework,选择安装。装好以后就应该是这么一个样子:ui
这里用的是Entity Framework6,由于这个版本支持了枚举类型,在此次的项目里也有涉及this
前面已经说了,到目前为止咱们尚未数据库。所以咱们须要写标准的.NET类来定义领域模型并帮咱们生成数据库spa
新建一个Entities文件夹并建立5个类(“Student”,“Course”,“Subject”,“Tutor”,“Enrollment”),这几个简单的实体类将为咱们建立数据库3d
public class Course { public Course() { Enrollments = new List<Enrollment>(); CourseTutor = new Tutor(); CourseSubject = new Subject(); } public int Id { get; set; } public string Name { get; set; } public Double Duration { get; set; } public string Description { get; set; } public Tutor CourseTutor { get; set; } public Subject CourseSubject { get; set; } public ICollection<Enrollment> Enrollments { get; set; } } public class Enrollment { public Enrollment() { Student = new Student(); Course = new Course(); } public int Id { get; set; } public DateTime EnrollmentDate { get; set; } public Student Student { get; set; } public Course Course { get; set; } } public class Student { public Student() { Enrollments = new List<Enrollment>(); } public int Id { get; set; } public string Email { get; set; } public string UserName { get; set; } public string Password { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Gender Gender { get; set; } public DateTime DateOfBirth { get; set; } public DateTime? RegistrationDate { get; set; } public DateTime? LastLoginDate { get; set; } public ICollection<Enrollment> Enrollments { get; set; } } public class Subject { public Subject() { Courses = new List<Course>(); } public int Id { get; set; } public string Name { get; set; } public ICollection<Course> Courses; } public class Tutor { public Tutor() { Courses = new List<Course>(); } public int Id { get; set; } public string Email { get; set; } public string UserName { get; set; } public string Password { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Gender Gender { get; set; } public ICollection<Course> Courses; }
不难发现,上面咱们建立的Model并无从任何基类继承,也没有打任何attributes。有哪些标准的话会使咱们的数据访问拥有更好的延展性,这样的话咱们就能够专心地去处理业务而不用过多地考虑数据的持久化。
Entity framework Code First默认使用一种叫“约定大于配置”的方式来为咱们建立数据库(经过POCO类映射数据库的表,字段的数据类型,外键等)。在一些简单的应用程序中来讲是很是好用的。但在咱们的项目中将使用Fluent API配置咱们自定义的数据库生成规则——“配置覆盖约定”
当咱们决定使用配置来覆盖默认规则时,咱们能够为每张表的字段配置数据类型,是否可空,表与表之间外键关系,主键以及标识列等等。
因而乎咱们建立一个“Mappers”文件夹,新建5个继承自System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<T>的类。分别命名为: “CourseMapper”, “EnrollmentMapper”, “StudentMapper”, “SubjectMapper”, and “TutorMapper”
class CourseMapper : EntityTypeConfiguration<Course> { public CourseMapper() { this.ToTable("Courses"); this.HasKey(c => c.Id); this.Property(c => c.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(c => c.Id).IsRequired(); this.Property(c => c.Name).IsRequired(); this.Property(c => c.Name).HasMaxLength(255); this.Property(c => c.Duration).IsRequired(); this.Property(c => c.Description).IsOptional(); this.Property(c => c.Description).HasMaxLength(1000); this.HasRequired(c => c.CourseSubject).WithMany().Map(s => s.MapKey("SubjectID")); this.HasRequired(c => c.CourseTutor).WithMany().Map(t => t.MapKey("TutorID")); } } class EnrollmentMapper : EntityTypeConfiguration<Enrollment> { public EnrollmentMapper() { this.ToTable("Enrollments"); this.HasKey(e => e.Id); this.Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(e => e.Id).IsRequired(); this.Property(e => e.EnrollmentDate).IsRequired(); this.Property(e => e.EnrollmentDate).HasColumnType("smalldatetime"); this.HasOptional(e => e.Student).WithMany(e => e.Enrollments).Map(s => s.MapKey("StudentID")).WillCascadeOnDelete(false); this.HasOptional(e => e.Course).WithMany(e => e.Enrollments).Map(c => c.MapKey("CourseID")).WillCascadeOnDelete(false); } } class StudentMapper : EntityTypeConfiguration<Student> { public StudentMapper() { this.ToTable("Students"); this.HasKey(s => s.Id); this.Property(s => s.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(s => s.Id).IsRequired(); this.Property(s => s.Email).IsRequired(); this.Property(s => s.Email).HasMaxLength(255); this.Property(s => s.Email).IsUnicode(false); this.Property(s => s.UserName).IsRequired(); this.Property(s => s.UserName).HasMaxLength(50); this.Property(s => s.UserName).IsUnicode(false); this.Property(s => s.Password).IsRequired(); this.Property(s => s.Password).HasMaxLength(255); this.Property(s => s.FirstName).IsRequired(); this.Property(s => s.FirstName).HasMaxLength(50); this.Property(s => s.LastName).IsRequired(); this.Property(s => s.LastName).HasMaxLength(50); this.Property(s => s.Gender).IsOptional(); this.Property(s => s.DateOfBirth).IsRequired(); this.Property(s => s.DateOfBirth).HasColumnType("smalldatetime"); this.Property(s => s.RegistrationDate).IsOptional(); this.Property(s => s.RegistrationDate).HasColumnType("smalldatetime"); this.Property(s => s.LastLoginDate).IsOptional(); this.Property(s => s.LastLoginDate).HasColumnType("smalldatetime"); } } class SubjectMapper : EntityTypeConfiguration<Subject> { public SubjectMapper() { this.ToTable("Subjects"); this.HasKey(s => s.Id); this.Property(s => s.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(s => s.Id).IsRequired(); this.Property(s => s.Name).IsRequired(); this.Property(s => s.Name).HasMaxLength(255); } } class TutorMapper : EntityTypeConfiguration<Tutor> { public TutorMapper() { this.ToTable("Tutors"); this.HasKey(s => s.Id); this.Property(s => s.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.Property(s => s.Id).IsRequired(); this.Property(s => s.Email).IsRequired(); this.Property(s => s.Email).HasMaxLength(255); this.Property(s => s.Email).IsUnicode(false); this.Property(s => s.UserName).IsRequired(); this.Property(s => s.UserName).HasMaxLength(50); this.Property(s => s.UserName).IsUnicode(false); this.Property(s => s.Password).IsRequired(); this.Property(s => s.Password).HasMaxLength(255); this.Property(s => s.FirstName).IsRequired(); this.Property(s => s.FirstName).HasMaxLength(50); this.Property(s => s.LastName).IsRequired(); this.Property(s => s.LastName).HasMaxLength(50); this.Property(s => s.Gender).IsOptional(); } }
看看上面的代码,不难发现咱们为每一个POCO类属性(数据类型,是否为空,主键标识,外键关系)都作了配置。这些配置最终会影响咱们数据库中表的建立。
关于具体Fluent API的配置方法,能够参考:http://www.cnblogs.com/hyl8218/archive/2011/10/10/2205240.html
如今咱们要建立一个LearningContext类继承自System.Data.Entity.DbContext:
public class LearningContext : DbContext { public LearningContext() : base("eLearningConnection") { Configuration.ProxyCreationEnabled = false; Configuration.LazyLoadingEnabled = false; Database.SetInitializer(new MigrateDatabaseToLatestVersion<LearningContext, LearningContextMigrationConfiguration>()); } public DbSet<Course> Courses { get; set; } public DbSet<Enrollment> Enrollments { get; set; } public DbSet<Student> Students { get; set; } public DbSet<Subject> Subjects { get; set; } public DbSet<Tutor> Tutors { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new StudentMapper()); modelBuilder.Configurations.Add(new SubjectMapper()); modelBuilder.Configurations.Add(new TutorMapper()); modelBuilder.Configurations.Add(new CourseMapper()); modelBuilder.Configurations.Add(new EnrollmentMapper()); base.OnModelCreating(modelBuilder); } }
这个LearningContext类主要有三大任务
1.把咱们定义的POCO类做为DBSet的属性对外公开,这意味着每个POCO类都将被转化为数据库中的表
2.重写OnModelCreating方法将咱们为POCO类自定义的映射规则添加到DbModelBuilder配置中
3.在LearningContext的构造函数中咱们作了2件事:
(1)将ProxyCreationEnabled和LazyLoadingEnabled 2个属性设为false(默认都是true)。延迟加载(LazyLoading)主要是指当对象间的关联配置成导航属性暴露给外界的时候,那么这个属性的会在用到它的时候(在前台foreach的时候)才加载,那么极有可能成为性能杀手,咱们项目中但愿当即加载。而ProxyCreation是配合LazyLoading一块儿用的,所以当咱们把这两个属性值设为false时,那么“LearningContext”就不会去加载导航属性除非调用了“Include”方法(下一章会有演示)。
(2)为数据库配置初始化和迁移策略,若是模型的属性被改变(添加了新的属性),就把数据库迁移到最新版本。为了实现这个功能,咱们新建一个类LearningContextMigrationConfiguration继承自System.Data.Entity.Migrations.DbMigrationsConfiguration<TContext>
class LearningContextMigrationConfiguration : DbMigrationsConfiguration<LearningContext> { public LearningContextMigrationConfiguration() { this.AutomaticMigrationsEnabled = true; this.AutomaticMigrationDataLossAllowed = true; } #if DEBUG protected override void Seed(LearningContext context) { new LearningDataSeeder(context).Seed(); } #endif }
LearningContextMigrationConfiguration这个类主要有2个任务:
(1)在构造函数中,咱们设置AutomaticMigrationsEnabled 属性为true,那么就意味着EF会为咱们自动迁移数据库而不考虑版本问题。同时咱们把AutomaticMigrationDataLossAllowed属性也设为true但这样作在开发环境中是很危险的,由于若是这个属性设为false时,一旦数据库在自动迁移时发生数据丢失,那么就会抛出一个异常。但在此次的系列中咱们确保没问题。
(2)重写Seed方法来为咱们的数据库添加初始数据,这个函数在咱们应用程序每次启动时执行。“LearningDataSeeder”这个类主要用于提供须要的数据,具体代码在本章结束时提供。
到目前为止,咱们已经把用于建立数据库的Model和配置都实现了。在这个时候咱们应该想想:数据访问层是否已经完成?当咱们使用Web Api操做的时候是否方便 快捷 高效?咱们是否应该对已有的数据访问层再作一次封装?。。。 所以下一章咱们将使用“Repository”模式应用在咱们的项目中。
随堂代码:http://yun.baidu.com/share/link?shareid=1763536438&uk=17559114