使用ASP.NET WEB API構建基於REST風格的服務實戰系列教程(一)——使用EF6構建數據庫及模型

系列導航地址http://www.cnblogs.com/fzrain/p/3490137.htmlhtml

使用Entity Framework Code First模式構建數據庫對象

已經決定使用EF CodeFirst來建立數據庫了,所以咱們使用POCO類(「Plain Old CLR Objects)來定義咱們的Model。咱們經過寫標準的.NET類來定義適合咱們API的領域模型。那些POCO類就會爲咱們建立數據庫。數據庫

咱們的培訓系統數據庫比較簡單,首先咱們須要有學生」Students」,導師」Tutors」,除此以外咱們還須要定義課程」Courses」以及科目」Subjects」。在咱們的系統中,咱們容許每一個學生去報名參加不一樣的課程app

下圖是咱們數據庫創建後的結果,我在這裏列出來的目的是爲了幫助你們更好的理解接下來建立的POCO類(圖不是很清晰,湊合看吧):ide

elearningdatabaseschema

第一步:建立一個空的類庫項目

打開VS,建立一個空的類庫項目函數

QQ截圖20131225114044

.NET Framewok版本的話我用的事4.5的,選4.0的也行性能

第二步:使用NuGet添加Entity Framework的引用

右擊引用->管理NuGet程序包->右上角輸入Entity Framework,選擇安裝。裝好以後就應該是這麼一個樣子:ui

QQ截圖20131225114613

這裏用的是Entity Framework6,由於這個版本支持了枚舉類型,在此次的項目裏也有涉及this

第三步:建立Model

前面已經說了,到目前爲止咱們尚未數據庫。所以咱們須要寫標準的.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

第五步:建立Context類來實現數據持久化

如今咱們要建立一個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

相關文章
相關標籤/搜索