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的就行呢。
相关文章
相关标签/搜索