zoukankan      html  css  js  c++  java
  • Entity Framework 实体框架的形成之旅--Code First的框架设计(5)

    在前面几篇介绍了Entity Framework 实体框架的形成过程,整体框架主要是基于Database First的方式构建,也就是利用EDMX文件的映射关系,构建表与表之间的关系,这种模式弹性好,也可以利用图形化的设计器来设计表之间的关系,是开发项目较多采用的模式,不过问题还是这个XML太过复杂,因此有时候也想利用Code First模式构建整个框架。本文主要介绍利用Code First 来构建整个框架的过程以及碰到的问题探讨。

     1、基于SqlServer的Code First模式

    为了快速了解Code First的工作模式,我们先以微软自身的SQLServer数据库进行开发测试,我们还是按照常规的模式先构建一个标准关系的数据库,如下所示。

    这个表包含了几个经典的关系,一个是自引用关系的Role表,一个是User和Role表的多对多关系,一个是User和UserDetail之间的引用关系。

    一般情况下,能处理好这几种关系,基本上就能满足大多数项目上的要求了。这几个表的数据库脚本如下所示。

    create table dbo.Role (
       ID                   nvarchar(50)         not null,
       Name                 nvarchar(50)         null,
       ParentID             nvarchar(50)         null,
       constraint PK_ROLE primary key (ID)
    )
    go
    
    create table dbo."User" (
       ID                   nvarchar(50)         not null,
       Account              nvarchar(50)         null,
       Password             nvarchar(50)         null,
       constraint PK_USER primary key (ID)
    )
    go
    
    create table dbo.UserDetail (
       ID                   nvarchar(50)         not null,
       User_ID              nvarchar(50)         null,
       Name                 nvarchar(50)         null,
       Sex                  int                  null,
       Birthdate            datetime             null,
       Height               decimal              null,
       Note                 ntext                null,
       constraint PK_USERDETAIL primary key (ID)
    )
    go
    
    create table dbo.UserRole (
       User_ID              nvarchar(50)         not null,
       Role_ID              nvarchar(50)         not null,
       constraint PK_USERROLE primary key (User_ID, Role_ID)
    )
    go
    
    alter table dbo.Role
       add constraint FK_ROLE_REFERENCE_ROLE foreign key (ParentID)
          references dbo.Role (ID)
    go
    
    alter table dbo.UserDetail
       add constraint FK_USERDETA_REFERENCE_USER foreign key (User_ID)
          references dbo."User" (ID)
    go
    
    alter table dbo.UserRole
       add constraint FK_USERROLE_REFERENCE_ROLE foreign key (Role_ID)
          references dbo.Role (ID)
    go
    
    alter table dbo.UserRole
       add constraint FK_USERROLE_REFERENCE_USER foreign key (User_ID)
          references dbo."User" (ID)
    go

    我们采用刚才介绍的Code Frist方式来构建实体框架,如下面几个步骤所示。

    1)选择来自数据库的Code First方式。

    2)选择指定的数据库连接,并选择对应的数据库表,如下所示(包括中间表UserRole)。

    生成项目后,项目工程会增加几个类,包括Role实体类,User实体类,UserDetail实体类(没有中间表UserRole的实体类),还有一个是包含这些实体类的数据库上下文关系,它们的表之间的关系,是通过代码指定的,没有了EDMX文件了。

    几个类文件的代码如下所示,其中实体类在类定义的头部,增加了[Table("Role")]的说明,表明了这个实体类和数据库表之间的关系。

        [Table("Role")]
        public partial class Role
        {
            public Role()
            {
                Children = new HashSet<Role>();
                Users = new HashSet<User>();
            }
    
            [StringLength(50)]
            public string ID { get; set; }
    
            [StringLength(50)]
            public string Name { get; set; }
    
            [StringLength(50)]
            public string ParentID { get; set; }
    
            public virtual ICollection<Role> Children { get; set; }
    
            public virtual Role Parent { get; set; }
    
            public virtual ICollection<User> Users { get; set; }
        }

    其他类如下所示。

        [Table("User")]
        public partial class User
        {
            public User()
            {
                UserDetails = new HashSet<UserDetail>();
                Roles = new HashSet<Role>();
            }
    
            [StringLength(50)]
            public string ID { get; set; }
    
            [StringLength(50)]
            public string Account { get; set; }
    
            [StringLength(50)]
            public string Password { get; set; }
    
            public virtual ICollection<UserDetail> UserDetails { get; set; }
    
            public virtual ICollection<Role> Roles { get; set; }
        }
        [Table("UserDetail")]
        public partial class UserDetail
        {
            [StringLength(50)]
            public string ID { get; set; }
    
            [StringLength(50)]
            public string User_ID { get; set; }
    
            [StringLength(50)]
            public string Name { get; set; }
    
            public int? Sex { get; set; }
    
            public DateTime? Birthdate { get; set; }
    
            public decimal? Height { get; set; }
    
            [Column(TypeName = "ntext")]
            public string Note { get; set; }
    
            public virtual User User { get; set; }
        }

    还有一个就是生成的数据库上下文的类。

        public partial class DbEntities : DbContext
        {
            public DbEntities() : base("name=Model1")
            {
            }
            public virtual DbSet<Role> Roles { get; set; }
            public virtual DbSet<User> Users { get; set; }
            public virtual DbSet<UserDetail> UserDetails { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Role>()
                    .HasMany(e => e.Children)
                    .WithOptional(e => e.Parent)
                    .HasForeignKey(e => e.ParentID);
    
                modelBuilder.Entity<Role>()
                    .HasMany(e => e.Users)
                    .WithMany(e => e.Roles)
                    .Map(m => m.ToTable("UserRole"));
    
                modelBuilder.Entity<User>()
                    .HasMany(e => e.UserDetails)
                    .WithOptional(e => e.User)
                    .HasForeignKey(e => e.User_ID);
    
                modelBuilder.Entity<UserDetail>()
                    .Property(e => e.Height)
                    .HasPrecision(18, 0);
            }
        }

    上面这个数据库上下文的操作类,通过在OnModelCreating函数里面使用代码方式指定了几个表之间的关系,代替了EDMX文件的描述。

    这样好像看起来比EDMX文件简单了很多,感觉很开心,一切就那么顺利。

    如果我们使用这个数据库上下文进行数据库的插入,也是很顺利的执行,并包含了的多个表之间的关系处理,代码如下所示。

            private void NormalTest()
            {
                DbEntities db = new DbEntities();
                Role role = new Role() { ID = Guid.NewGuid().ToString(), Name = "test33" };
    
                User user = new User() { ID = Guid.NewGuid().ToString(), Account = "test33", Password = "test33" };
                UserDetail detail = new UserDetail() { ID = Guid.NewGuid().ToString(), Name = "userName33", Sex = 1, Note = "测试内容33", Height = 175 };
                user.UserDetails.Add(detail);
    
                role.Users.Add(user);
    
                db.Roles.Add(role);
                db.SaveChanges();
    
                List<Role> list = db.Roles.ToList();
            }

    我们发现,通过上面代码的操作,几个表都写入了数据,已经包含了他们之间的引用关系了。

    2、基于泛型的仓储模式实体框架的提炼

    为了更好对不同数据库的封装,我引入了前面介绍的基于泛型的仓储模式实体框架的结构,希望后面能够兼容多种数据库的支持,最终构建代码的分层结构如下所示。

    使用这种框架的分层,相当于为各个数据库访问提供了统一标准的通用接口,为我们利用各种强大的基类快速实现各种功能提供了很好的保障。使用这种分层的框架代码如下所示。

            private void FrameworkTest()
            {
                Role role = new Role() { ID = Guid.NewGuid().ToString(), Name = "test33" };
    
                User user = new User() { ID = Guid.NewGuid().ToString(), Account = "test33", Password = "test33" };
                UserDetail detail = new UserDetail() { ID = Guid.NewGuid().ToString(), Name = "userName33", Sex = 1, Note = "测试内容33", Height = 175 };
                user.UserDetails.Add(detail);
    
                role.Users.Add(user);
    
                IFactory.Instance<IRoleBLL>().Insert(role);
    
                ICollection<Role> list = IFactory.Instance<IRoleBLL>().GetAll();
    
            }

    我们发现,这部分代码执行的效果和纯粹使用自动生成的数据库上下文DbEntities 来操作数据库一样,能够写入各个表的数据,并添加了相关的应用关系。

    满以为这样也可以很容易扩展到Oracle数据库上,但使用SQLServer数据库生成的实体类,在Oracle数据库访问的时候,发现它生成的实体类名称全部是大写,一旦修改为Camel驼峰格式的字段,就会出现找不到对应表字段的错误。

    寻找了很多解决方案,依旧无法有效避免这个问题,因为Oracle本身的表或者字段名称是大小写敏感的,关于Oracle这个问题,先关注后续解决吧,不过对于如果不考虑支持多种数据库的话,基于SQLServer数据库的Code First构建框架真的还是比较方便,我们不用维护那个比较麻烦的EDMX文件,只需要在代码函数里面动态添加几个表之间的关系即可。

    这个系列文章索引如下:

    Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)

    Entity Framework 实体框架的形成之旅--利用Unity对象依赖注入优化实体框架(2) 

    Entity Framework 实体框架的形成之旅--基类接口的统一和异步操作的实现(3)

    Entity Framework 实体框架的形成之旅--实体数据模型 (EDM)的处理(4)

    Entity Framework 实体框架的形成之旅--Code First的框架设计(5) 

  • 相关阅读:
    设置GridView、DataGrid 以提供thead、tbody等标签
    SqlCommandBuilder 可批量新增与修改数据
    js中的截流
    react代码分离方案
    redux在react中的使用
    react 生命周期
    react 函数bind(this)的三种方式
    react 三种组件定义方式
    linux系统下nginx安装目录和nginx.conf配置文件目录
    react component lifecycle
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/4352178.html
Copyright © 2011-2022 走看看