• TinyFrame开篇:基于CodeFirst的ORM


    前言

    做项目的这段时间,由于比较忙,一直没有机会闲下来思考。正好趁目前手头活儿轻松点,就花了一两天时间搭建了一个比较简单的框架,名称暂时就叫做:TinyFrame吧。顾名思义,就是微框架的意思。虽然这个框架很小,但是包含了ORM,IOC,AOP以及Restful service支持。真所谓麻雀虽小,但是五脏俱全。今天主要讲解的就是基于CodeFirst的ORM实现方式。

    在传统的开发思维中,我们一般都是先建立好数据库,然后布局各种Model类,最后调用。这种方式被称为DbFirst方式,也就是先有DataBase,然后再布局逻辑。而CodeFirst相反,是先建立好model,建立好需要的字段,然后由EntityFramework来自动生成数据库的表,看上去有点像DDD的开发方式。

    下面就开始吧。

    实现步骤解说

    首先,我们以一个简易的图书借阅为例:在图书馆中,图书是有分类的,比如属于计算机类,还是属于文学类;图书是有存放位置的,比如存放于图书馆一楼,还是二楼;图书是有自身属性的,比如图书名称,图书作者等;图书借阅是需要人来参与的,所以会有学生这个集体;图书借阅需要知道谁借了什么书,什么时候到期等,是需要一个数据存储中心的。所以,综上所述,可以建立如下的Model来。

    来看看数据关系图:

    QQ截图20140306223542

    这里可以看到总共有五张表,他们的Model类创建如下:

    Book实体类,主要用来描述图书的本身属性,一本书只能放置在一个地方,只能属于一个种类,有BookTypeID和BookPlaceID作为外键约束:

       1:  namespace BookStore.Data
       2:  {
       3:      public class Book
       4:      {
       5:          public Book()
       6:          {
       7:              BookType = new BookType();
       8:              BookPlace = new BookPlace();
       9:          }
      10:   
      11:          public int ID { get; set; }
      12:          public string Name { get; set; }
      13:          public string Author { get; set; }
      14:          public string Publishment { get; set; }
      15:          public int BookTypeID { get; set; }
      16:          public int BookPlaceID { get; set; }
      17:   
      18:          public BookType BookType { get; set; }
      19:          public BookPlace BookPlace { get; set; }
      20:      }
      21:  }

    BookLend实体类,主要用来描述哪位学生借阅了什么书籍,一个学生可以借阅多本书籍,所以里面放了一个Student实体类,对应着多个Books,有BookID和StudentID作为外键约束:

       1:  namespace BookStore.Data
       2:  {
       3:      public class BookLend
       4:      {
       5:          public BookLend()
       6:          {
       7:              Student = new Student();
       8:              Books = new List<Book>();
       9:          }
      10:          public int ID { get; set; }
      11:          public int LendDays { get; set; }
      12:          public DateTime LendDate { get; set; }
      13:          public int BookID { get; set; }
      14:          public int StudentID { get; set; }
      15:   
      16:          public IList<Book> Books { get; set; }
      17:          public Student Student { get; set; }
      18:      }
      19:  }

    BookPlace实体类,主要用来描述图书放置的位置:

       1:  namespace BookStore.Data
       2:  {
       3:      public class BookPlace
       4:      {
       5:          public int ID { get; set; }
       6:          public string Position { get; set; }
       7:      }
       8:  }

    BookType实体类,主要用来描述图书种类:

       1:  namespace BookStore.Data
       2:  {
       3:      public class BookType
       4:      {
       5:          public int ID { get; set; }
       6:          public string Name { get; set; }
       7:      }
       8:  }

    Student实体类,主要用来描述学生属性:

       1:  namespace BookStore.Data
       2:  {
       3:      public class Student
       4:      {
       5:          public int ID { get; set; }
       6:          public string Name { get; set; }
       7:          public string Number { get; set; }
       8:          public string Major { get; set; }
       9:          public string TelPhone { get; set; }
      10:          public string Address { get; set; }
      11:          public Gender Gender { get; set; }
      12:      }
      13:  }

    这里还有个附加的枚举性别选项:

       1:  namespace BookStore.Data
       2:  {
       3:      public enum Gender
       4:      {
       5:          Male = 0,    //男
       6:          Female = 1    //女
       7:      }
       8:  }

    到这里,我们的Model就创建好了。

    然后我们来创建ModelMapper。这种Mapper主要是用来设置各个字段的属性的。

    BookMapper类:

       1:  namespace BookStore.Data
       2:  {
       3:      public class BookMapper:EntityTypeConfiguration<Book>
       4:      {
       5:          public BookMapper()
       6:          {
       7:              this.ToTable("Book");
       8:   
       9:              this.HasKey(c => c.ID);
      10:              this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      11:              this.Property(c => c.ID).IsRequired();
      12:   
      13:              this.Property(c => c.Name).HasMaxLength(255);
      14:              this.Property(c => c.Name).IsRequired();
      15:   
      16:              this.Property(c => c.Author).HasMaxLength(255);
      17:              this.Property(c => c.Author).IsOptional();
      18:   
      19:              this.Property(c => c.Publishment).HasMaxLength(255);
      20:              this.Property(c => c.Publishment).IsRequired();
      21:   
      22:              this.HasRequired(c => c.BookType).WithMany().HasForeignKey(s => s.BookTypeID);
      23:              this.HasRequired(c => c.BookPlace).WithMany().HasForeignKey(s => s.BookPlaceID);
      24:   
      25:          }
      26:      }
      27:  }

    第7行代码表示:对于Book实体类来说,最终将会在数据库中为其创建名称为Book的数据表。

    第9行代码表示:这是一个主键。

    第10行代码表示:这个主键是自增的。

    第11行代码表示:这个主键是不能为空的。

    第13,14行代码表示:这是一个普通属性,字段最大长度为255,不能为空。

    第16,17行代码表示:这是一个普通属性,字段最大长度为255,可以为空。

    第22,23行代码表示:在BookType表和BookPlace表中存在外键依赖。需要注意的是,如果Model类中定义了BookTypeID和BookPlaceID,这里一定要用HasForeignKey方法来指明。否则,可以利用Map方法来隐式指定。

    接下来的Mapper就不一一解释了:

    BookLendManager类:

       1:  namespace BookStore.Data
       2:  {
       3:      public class BookLendMapper : EntityTypeConfiguration<BookLend>
       4:      {
       5:          public BookLendMapper()
       6:          {
       7:              this.ToTable("BookLend");
       8:   
       9:              this.HasKey(c=>c.ID);
      10:              this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      11:              this.Property(c => c.ID).IsRequired();
      12:   
      13:              this.Property(c => c.LendDays).IsRequired();
      14:   
      15:              this.Property(c => c.LendDate).IsRequired();
      16:              this.Property(s => s.LendDate).HasColumnType("smalldatetime");
      17:   
      18:              this.HasRequired(c => c.Books).WithMany().HasForeignKey(s=>s.BookID);
      19:              this.HasRequired(c => c.Student).WithMany().HasForeignKey(s =>s.StudentID);
      20:          }
      21:      }
      22:  }

    BookPlaceMapper类:

       1:  namespace BookStore.Data
       2:  {
       3:      public class BookPlaceMapper : EntityTypeConfiguration<BookPlace>
       4:      {
       5:          public BookPlaceMapper()
       6:          {
       7:              this.ToTable("BookPlace");
       8:   
       9:              this.HasKey(c=>c.ID);
      10:              this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      11:              this.Property(c => c.ID).IsRequired();
      12:   
      13:              this.Property(c => c.Position).IsRequired();
      14:              this.Property(c => c.Position).HasMaxLength(255);
      15:          }
      16:      }
      17:  }

    BookTypeMapper类:

       1:  namespace BookStore.Data.Mapper
       2:  {
       3:      public class BookTypeMapper : EntityTypeConfiguration<BookType>
       4:      {
       5:          public BookTypeMapper()
       6:          {
       7:              this.ToTable("BookType");
       8:   
       9:              this.HasKey(c => c.ID);
      10:              this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      11:              this.Property(c => c.ID).IsRequired();
      12:   
      13:              this.Property(c => c.Name).IsRequired();
      14:              this.Property(c => c.Name).HasMaxLength(255);
      15:          }
      16:      }
      17:  }

    StudentMapper类:

       1:  namespace BookStore.Data
       2:  {
       3:      public class StudentMapper : EntityTypeConfiguration<Student>
       4:      {
       5:          public StudentMapper()
       6:          {
       7:              this.ToTable("Student");
       8:   
       9:              this.HasKey(c=>c.ID);
      10:              this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
      11:              this.Property(c => c.ID).IsRequired();
      12:   
      13:              this.Property(c => c.Name).IsRequired();
      14:              this.Property(c => c.Name).HasMaxLength(255);
      15:   
      16:              this.Property(c => c.Number).IsRequired();
      17:              this.Property(c => c.Number).HasMaxLength(12);
      18:   
      19:              this.Property(c => c.Major).IsOptional();
      20:              this.Property(c => c.Major).HasMaxLength(255);
      21:   
      22:              this.Property(c => c.Address).IsOptional();
      23:              this.Property(c => c.Address).HasMaxLength(255);
      24:   
      25:              this.Property(c => c.Gender).IsRequired();
      26:          }
      27:      }
      28:  }

    当我们写完这些mapper的时候,工作已经完成一大半了,剩下的就是如何将其自动创建到数据库中的问题了。

    这里我们需要用到EF的SetInitializer方法:

    首先创建一个BookContext,继承自DbContext类,然后在构造中书写以下代码,以便实现数据实体类迁移:

       1:   public BookContext()
       2:              : base("BookConnection")
       3:          {
       4:              Configuration.ProxyCreationEnabled = false;
       5:              Configuration.LazyLoadingEnabled = false;
       6:              Database.SetInitializer(new MigrateDatabaseToLatestVersion<BookContext, BookContextMConfig>());
       7:          }
    然后重写其OnModelCreating方法,将Mapper类加入到ModelBuilder中:
       1:     protected override void OnModelCreating(DbModelBuilder modelBuilder)
       2:          {
       3:              modelBuilder.Configurations.Add(new BookMapper());
       4:              modelBuilder.Configurations.Add(new BookLendMapper());
       5:              modelBuilder.Configurations.Add(new BookTypeMapper());
       6:              modelBuilder.Configurations.Add(new BookPlaceMapper());
       7:              modelBuilder.Configurations.Add(new StudentMapper());
       8:              modelBuilder.Configurations.Add(new ManagerMapper());
       9:              base.OnModelCreating(modelBuilder);
      10:          }

    其中需要注意BookContextMConfig这个类,它需要继承DbMigrationsConfiguration类:

       1:      public class BookContextMConfig : DbMigrationsConfiguration<BookContext>
       2:      {
       3:          public BookContextMConfig()
       4:          {
       5:              this.AutomaticMigrationDataLossAllowed = true;
       6:              this.AutomaticMigrationsEnabled = true;
       7:          }
       8:      }

    这样我们的一个完整的ORM就封装好了.通过访问BookContext对象,我们就可以从数据库获得与之对应的实体类.

    最后还要注意,需要将数据库连接写到配置文件中:

      <connectionStrings>
        <add name="BookConnection" connectionString="server=180-server;database=BookConnection;uid=sa;pwd=*****" providerName="System.Data.SqlClient" />
      </connectionStrings>

    当这个实例运行起来的时候,我们就可以看到数据库中的表被自动创建了.并且实例化BookContext对象之后,我们可以获取到所有的Books,Students,BookTypes,BookPlaces,BookLends。

    下一节主要来说明如何进行IOC注入。

  • 相关阅读:
    ylbtech-dbs-m-QQ邮箱
    ylbtech-Bill(发票管理)-数据库设计
    ylbtech-Recode(记录)-数据库设计
    ylbtech-LanguageSamples-Yield
    ylbtech-LanguageSamples-XMLdoc
    ylbtech-LanguageSamples-Versioning(版本控制)
    线程局部变量的使用
    守护线程的创建和运行
    有助于改善性能的技巧
    使用NIO提升性能
  • 原文地址:https://www.cnblogs.com/scy251147/p/3585506.html
Copyright © 2020-2023  润新知