EF6 CodeFirst+Repository+Ninject+MVC4+EasyUI實踐(四)

前言

 

  • 這一篇,咱們終於到了講解Entity Framework CodeFirst 的時刻了,首先建立實體對象模型,而後會經過配置Fluent API的方式來對實體對象模型進行完整的數據庫映射操做。
  • 此篇幅中會涉及到一些Entity Frame的相關概念,會給出初步的解釋。若是須要詳細瞭解,能夠查閱相關的幫助文檔。

 

EF實體對象模型的建立

 

  • EF的實體對象模型大都採用POCO類的方式建立。POCO的全稱爲Plain_Old_CLR_Object(簡單傳統CLR對象),是指那些沒有從任何類繼承,也沒有實現任何接口的簡單對象。EF CodeFirst能夠利用POCO類建立的實體對象來對數據庫進行映射,即咱們能夠經過編寫POCO類中的屬性和關聯POCO類的關係的方式完成對數據庫的映射操做。
  • 咱們知道數據庫的規範經過範式來進行約束,那咱們如何經過POCO類的方式來完成對數據庫映射以及約束的操做呢?首先咱們得把實體對象間的關係建立清楚,實體對象間的關係有三種:一對1、一對多、多對多。接下來咱們經過完成示例來演示如何建立這些示例。
  1. 打開解決方案的Entities工程,咱們把POCO類都創建在此工程下。沒有關注過此係列文章的朋友能夠在第二篇的末尾下載到解決方案工程文件。建立用戶類S_User,與此類關聯的對象有S_Role和S_Log,由於一個用戶只屬於某一個對象,一個用戶包含多條操做日誌。所以S_User的代碼以下:
  public class S_User
    {
        public S_User(){this.S_Logs = new List<S_Log>();}
        public long ID { get; set; }
        public long RoleID { get; set; }
        public string UserName { get; set; }
        public string UserPwd { get; set; }
        public string IsUse{ get; set; }
        public string Phone{ get; set; }
        public string Email{ get; set; }
        public string Remark { get; set; }
        public virtual S_Role S_Role { get; set; }
        public virtual ICollection<S_Log> S_Logs { get; set; }
    }

  2. 接下來是日誌類S_Log,與此關聯的對象有S_User,即一條日誌只屬於某一個用戶。所以S_Log的代碼以下:web

  public class S_Log
    {
        public long ID { get; set; }
        public long UserID { get; set; }
        public DateTime OperationDate { get; set; }
        public string  OperationMenu{ get; set; }
        public string  OperationType{ get; set; }
        public string Detail{ get; set; }
        public virtual S_User S_User { get; set; }
    }

  3. 接下來是角色類S_Role,與此關聯的對象有S_User和S_Menu,即一個角色能夠包含多個用戶,一個角色用戶能夠操做多個菜單頁面。數據庫

    所以S_Role代碼以下:mvc

  public class S_Role
    {
        public S_Role(){ this.S_Users = new List<S_User>();}
        public long ID { get; set; }
        public string RoleName { get; set; }
        public string Remark { get; set; }
        public virtual ICollection<S_User> S_Users { get; set; }
        public virtual ICollection<S_Menu> S_Menus { get; set; }
    }

  4. 接下來是菜單類S_Menu,與此類關聯的對象有S_Role,即一個菜單頁面能夠被多個角色用戶操做。可是S_Menu採用的是樹級結構的方式,app

   一個父級菜單包含多個子菜單,一個子菜單隻屬於某一個父級菜單。即子菜單的PID是父級菜單的ID,所以S_Menu的PID因該是S_Menu中IDasp.net

    的外鍵,因爲頂級的父級菜單是沒有父級菜單的,因此咱們能夠設置PID爲可空類型,S_Menu的代碼以下:數據庫設計

  public class S_Menu
    {
        public long ID { get; set; }
        public string MenuName { get; set; }
        public string Icon { get; set; }
        public string Link { get; set; }
        public string IsUse { get; set; }
        public int Level { get; set; }
        public int SerialNO { get; set; }
        public Nullable<long> PID { get; set; }
        public string Remark { get; set; }
        public virtual ICollection<S_Role> S_Roles { get; set; }
        public virtual S_Menu Parent { get; set; }
        public virtual ICollection<S_Menu> Children { get; set; }
    }

  5. 接下來是字典類S_TypeInfo,因爲沒有與之關聯的對象,所以,咱們只需簡單定義屬性就能夠呢。S_TypeInfo的代碼以下:ide

  public class S_TypeInfo
    {
        public long ID { get; set; }
        public string Type { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
        public string Remark { get; set; }
    }

  6. 注意:一對多關係中,咱們須要對外鍵的實體進行構造函數進行重載,好比角色中包含多個用戶,public S_Role(){this.S_Users = new 函數

   List<S_User>();}。多對多關係就不用進行此操做呢。還有一點就是外鍵必須顯示的設置,好比RoleID。由於後面在Fluent API映射數據時ui

        ,配置文件中須要用到。this

 

EF實體上下文的建立

 

  • 從Nuget上獲取EntityFramework,工程選擇Concrete,在「程序包管理器控制檯」中輸入Install-Package EntityFramework命令就能夠安裝了。

  

  

  • 在Concrete工程中添加EFDbContext類來構建領域實體上下文模型。設置咱們的數據庫鏈接名稱爲EFDbContext,修改Web項目下的web.config設置鏈接名稱爲EFDbContext,配置好數據庫鏈接字符串後,咱們才能夠鏈接數據庫。在數據庫映射的建立方法OnModelCreating中,咱們把映射的配置文件放到Mapper工程下,把數據庫的初始化操做放到Initializer工程下。EFDbContext代碼以下:
  public class EFDbContext : DbContext
    {
        public EFDbContext()
            : base("EFDbContext") { }

        public EFDbContext(string nameOrConnectionString)
            : base(nameOrConnectionString) {  }

        public DbSet<S_Log> S_Logs { get; set; }
        public DbSet<S_Menu> S_Menus { get; set; }
        public DbSet<S_Role> S_Roles { get; set; }
        public DbSet<S_TypeInfo> S_TypeInfos { get; set; }
        public DbSet<S_User> S_Users { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new S_LogMap());
            modelBuilder.Configurations.Add(new S_MenuMap());
            modelBuilder.Configurations.Add(new S_RoleMap());
            modelBuilder.Configurations.Add(new S_TypeInfoMap());
            modelBuilder.Configurations.Add(new S_UserMap());

            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();  //表中都統一設置禁用一對多級聯刪除
            modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); //表中都統一設置禁用多對多級聯刪除

            base.OnModelCreating(modelBuilder);
        }
    }

 

Fluent API配置數據庫映射

 

  • 其實若是不使用數據庫配置方式,EntityFramework也能夠將實體庫映射到數據庫文件中,只是可能達不到咱們預期的數據庫設計的目的,由於EntityFramework會採用默認的數據庫映射方式來生成數據庫。採用Fluent API能夠對數據庫進行詳細的配置,主要包括:
  1. 主鍵的設置
  2. 屬性的設置
  3. 表和字段的設置
  4. 關係的設置
  • 在採用Fluent API方式配置實體時,實體都繼承一個類型爲EntityTypeConfiguration的泛型類,只有繼承此類構建的實體才能夠在數據庫中映射出對應的約束條件。
  • 首先咱們來創建一下S_User的映射文件S_UserMap,代碼以下:
  public class S_UserMap : EntityTypeConfiguration<S_User>
    {
        public S_UserMap()
        {
            // Primary Key
            this.HasKey(t => t.ID);

            // Properties
            this.Property(t => t.UserName).IsRequired().HasMaxLength(20);
            this.Property(t => t.UserPwd).IsRequired().HasMaxLength(25);
            this.Property(t => t.IsUse).IsRequired().HasMaxLength(2);
            this.Property(t => t.Phone).IsOptional().HasMaxLength(11);
            this.Property(t => t.Email).IsOptional().HasMaxLength(25);
            this.Property(t => t.Remark).IsOptional().HasMaxLength(20);

            // Table & Column Mappings
            this.ToTable("S_User");
            this.Property(t => t.ID).HasColumnName("ID").HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.Property(t => t.UserName).HasColumnName("UserName");
            this.Property(t => t.UserPwd).HasColumnName("UserPwd");
            this.Property(t => t.IsUse).HasColumnName("IsUse");
            this.Property(t => t.Phone).HasColumnName("Phone");
            this.Property(t => t.Email).HasColumnName("Email");
            this.Property(t => t.Remark).HasColumnName("Remark");
            this.Property(t => t.RoleID).HasColumnName("RoleID");

            // Relationships
            this.HasRequired(t => t.S_Role).WithMany(t => t.S_Users).HasForeignKey(d => d.RoleID);
        }
    }

 

  1. S_UserMap繼承了EntityTypeConfiguration<S_User>的泛型類
  2. HasKey用來指定那個屬性爲主鍵
  3. 在Properties設置中,IsRequired用來設定屬性爲必須字段,不可爲空。HasMaxLength用來設置字段的長度。IsOptional用來設定屬性爲可選字段,能夠爲空。
  4. ToTable能夠用來設置表的名稱,HasColumnName用來設定字段的名稱。若是你想數據庫字段名和實體類中的屬性名不同,能夠在此進行設置。HasDatabaseGeneratedOption用來表示字段列是否爲自增加,本示例中,咱們的主鍵採用的long類型的日期流水碼,不須要字段編號。因此設置爲none。
  5. 在Relationships中,因爲一個用戶只屬於一個角色,因此RoleID就爲S_User對象的外鍵,配置外鍵的方式如代碼所示。

 

  • 接下來創建S_Role的映射文件S_RoleMap,代碼以下:
  public class S_RoleMap : EntityTypeConfiguration<S_Role>
    {
        public S_RoleMap()
        { 
              // Primary Key
            this.HasKey(t => t.ID);

            // Properties
            this.Property(t => t.RoleName).IsRequired().HasMaxLength(20);
            this.Property(t => t.Remark).IsRequired().HasMaxLength(200);

            // Table & Column Mappings
            this.ToTable("S_Role");
            this.Property(t => t.ID).HasColumnName("ID").HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.Property(t => t.RoleName).HasColumnName("RoleName");
            this.Property(t => t.Remark).HasColumnName("Remark");

            // Relationships
            this.HasMany(t => t.S_Menus)
            .WithMany(t => t.S_Roles)
            .Map(m =>
            {
                m.ToTable("S_RoleMenu");
                m.MapLeftKey("RoleID");
                m.MapRightKey("MenuID");
            });
        }
    }

 

  1. 其餘的設置在上面有說明呢,主要是Relationships,由於這裏的S_Role和S_Menu的關係爲多對多的關係,因此會產生一張關係表S_RoleMenu,而且是由RoleID和MenuID聯合產生的主鍵,而且RoleID爲S_Menu對象的外鍵,MenuID爲S_Role的外鍵。

 

  • 接下來創建S_Menu的映射文件S_MenuMap,代碼以下:
  public class S_MenuMap : EntityTypeConfiguration<S_Menu>
    {
        public S_MenuMap()
        {
             this.HasKey(t => t.ID);

            // Properties
            this.Property(t => t.MenuName).IsRequired().HasMaxLength(20);
            this.Property(t => t.Icon).IsRequired().HasMaxLength(20);
            this.Property(t => t.Link).IsRequired().HasMaxLength(20);
            this.Property(t => t.IsUse).IsOptional().HasMaxLength(2);
            this.Property(t => t.Remark).IsOptional().HasMaxLength(200);

            // Table & Column Mappings
            this.ToTable("S_Menu");
            this.Property(t => t.ID).HasColumnName("ID").HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
            this.Property(t => t.MenuName).HasColumnName("MenuName");
            this.Property(t => t.Icon).HasColumnName("Icon");
            this.Property(t => t.Link).HasColumnName("Link");
            this.Property(t => t.IsUse).HasColumnName("IsUse");
            this.Property(t => t.Level).HasColumnName("Level");
            this.Property(t => t.SerialNO).HasColumnName("SerialNO");
            this.Property(t => t.PID).HasColumnName("PID");
            this.Property(t => t.Remark).HasColumnName("Remark");

            // Relationships
            this.HasOptional(t => t.Parent)
             .WithMany(t => t.Children)
             .HasForeignKey(d => d.PID);
        }
    }

 

  1. 在此關係中,PID爲主鍵ID的外鍵,而且PID是爲可空類型,所以外鍵的設定方式和RoleID的設定方式同樣,只是把HasRequired變成了HasOptional,由於PID能夠爲空。

 

  • S_LogMap和S_TypeInfoMap就按照以上的方式建立就行呢。
  • 此示例中沒有一對一的關係,特此我也把一對一的關係設定方式以一個示例寫出來。

  

 

初始化數據庫

 

  • 由於數據庫是經過映射自動造成的,因此在數據庫初始化的時候,咱們給覺得生成的表添加一些默認數據,好比默認的角色用戶admin
  • 在工程Initializer下添加InitializerUserData類和DatabaseInitializer類,InitializerUserData類用來添加默認的角色用戶。而DatabaseInitializer類負責對數據庫的初始化,利用DbContext的Initialize來進行初始化。
  1. InitializerUserData的代碼以下:
  public class InitializerUserData : CreateDatabaseIfNotExists<EFDbContext>
    {
        protected override void Seed(EFDbContext context)
        {
            //添加默認角色
            S_Role role = new S_Role();
            role.ID = NewID.NewComb();
            role.RoleName = "管理員";
            role.Remark = "管理系統全部操做";

            //添加默認用戶
            long RoleID = role.ID;
            S_User user = new S_User();
            user.ID = NewID.NewComb();
            user.RoleID = RoleID;
            user.UserName ="admin";
            user.UserPwd=DESEncrypt.Encrypt("123");
            user.IsUse="";
            user.Phone="12345678901";
            user.Email="demo@hotmail.com";
            user.Remark = "系統管理員帳戶";
            user.S_Role = role;

            context.S_Roles.Add(role);
            context.S_Users.Add(user);
            context.SaveChanges();
        }
    }

   2. DatabaseInitializer的代碼以下:

  public static class DatabaseInitializer
    {
        public static void Initialize()
        {
            Database.SetInitializer(new InitializerUserData());
            using (var db = new EFDbContext())
            {
                db.Database.Initialize(false);
            }
        }
    }

  3. 注意:數據的初始化有三種方式,本示例選擇CreateDatabaseIfNotExists,也就是若是數據庫不存在咱們就進行建立,若是數據庫存在,

    咱們就只能經過數據遷移來進行對數據庫的修改操做。

   4. 在web工程的asp.net mvc 項目中的Global.asax添加對Initializer工程的引用,添加對數據庫初始化操做的註冊。以下顯示:  

  public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            DatabaseInitializer.Initialize();
        }
    }
  • 運行WEB工程,咱們就能夠把實體映射生成數據庫,打開數據庫管理器查看以下:

  

 

備註

 

  • 到此,咱們完成了POCO類的創建,以及經過Fluent API配置數據庫,設置數據庫初始化的值,完成了數據庫的映射操做。若是要對實體進行修改從新映射到數據庫,那麼就要使用數據遷移,這個我就很少說了。
  • 完成的示例代碼,我會放到網盤,不過目前的代碼就是博文提到的,能夠點擊下載。若是是本身搭建的,可能會遇到EntityFramework程序集加載不正確的錯誤。緣由是由於本地建立的MVC項目採用的是EntityFramework的5.0版本,而咱們經過Nuget獲取的是6.0版本,將工程集的EntityFramework5.0版本移除,從新加載6.0的就行呢。
相關文章
相關標籤/搜索