在前面几篇介绍了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(, );
}
}

上面这个数据库上下文的操作类,通过在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 = , Note = "测试内容33", Height = };
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 = , Note = "测试内容33", Height = };
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)

Entity Framework 实体框架的形成之旅--Code First的框架设计(5)的更多相关文章

  1. Entity Framework 实体框架的形成之旅--几种数据库操作的代码介绍(9)

    本篇主要对常规数据操作的处理和实体框架的处理代码进行对比,以便更容易学习理解实体框架里面,对各种数据库处理技巧,本篇介绍几种数据库操作的代码,包括写入中间表操作.联合中间表获取对象集合.递归操作.设置 ...

  2. Entity Framework 实体框架的形成之旅--界面操作的几个典型的处理(8)

    在上篇随笔<Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合>里面,介绍了在Entity Framework 实体框架里面引入了 ...

  3. Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合

    在使用Entity Framework 实体框架的时候,我们大多数时候操作的都是实体模型Entity,这个和数据库操作上下文结合,可以利用LINQ等各种方便手段,实现起来非常方便,一切看起来很美好.但 ...

  4. Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)

    在前面的随笔<Entity Framework 实体框架的形成之旅--Code First的框架设计(5)>里介绍了基于Code First模式的实体框架的经验,这种方式自动处理出来的模式 ...

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

    在前面几篇关于Entity Framework 实体框架的介绍里面,已经逐步对整个框架进行了一步步的演化,以期达到统一.高效.可重用性等目的,本文继续探讨基于泛型的仓储模式实体框架方面的改进优化,使我 ...

  6. Entity Framework 实体框架的形成之旅--实体框架的开发的几个经验总结

    在前阵子,我对实体框架进行了一定的研究,然后把整个学习的过程开了一个系列,以逐步深入的方式解读实体框架的相关技术,期间每每碰到一些新的问题需要潜入研究.本文继续前面的主题介绍,着重从整体性的来总结一下 ...

  7. Entity Framework 实体框架的形成之旅--为基础类库接口增加单元测试,对基类接口进行正确性校验(10)

    本篇介绍Entity Framework 实体框架的文章已经到了第十篇了,对实体框架的各个分层以及基类的封装管理,已经臻于完善,为了方便对基类接口的正确性校验,以及方便对以后完善或扩展接口进行回归测试 ...

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

    在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...

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

    在本系列的第一篇随笔<Entity Framework 实体框架的形成之旅--基于泛型的仓储模式的实体框架(1)>中介绍了Entity Framework 实体框架的一些基础知识,以及构建 ...

随机推荐

  1. 【读书笔记】-- JavaScript数组

    数组是一段线性分配的内存,它通过整数计算偏移并访问其中的元素.大多数的语言都会要求一个数组的元素是相同类型,但JavaScript数组可以包含任意类型. var misc = ['string', n ...

  2. 开源中国(oschina)的Git托管

    一些废话 对于使用CVS/SVN出身的人来说,Git始终有点难上手(比如我),因为概念较多,一时理不清,尽管网上已经有很多教程,其实后来我发现,要掌握它的办法就是使用它,着手开始用它做源码管理,有问题 ...

  3. BIT祝威博客汇总(Blog Index)

    +BIT祝威+悄悄在此留下版了个权的信息说: 关于硬件(Hardware) <穿越计算机的迷雾>笔记 继电器是如何成为CPU的(1) 继电器是如何成为CPU的(2) 关于操作系统(Oper ...

  4. Springlake-02 权限&文档设置&Role设置&Folder设置&登录

    1. 权限 有3个默认的权限用户: 1.System Owner so 管理员权限全部:Type Setup; Group Setup; Form Setup; Role Setup; Share R ...

  5. jq源码立即执行函数的undefined参数

    函数参数是undefined,那这个undefined是个变量标识符还是值呢? <JS权威指南>函数一章讲过,未给函数形参传值的时候,该形参的值是undefined.所以形参undefin ...

  6. Javascript隐式转换

    乱想 javascript为什么需要隐式转换?如果没有会出现什么情况? 找了一圈没有看到关于这个的讨论,只好自己研究了,可能不一定正确,自行辨知. 郁闷就是郁闷在好好的,为什么要搞个隐式转换,一般来讲 ...

  7. Atitit截屏功能的设计解决方案

    Atitit截屏功能的设计解决方案 自己实现.... 使用快捷键.. 弹出自己的win,,背景是屏幕快照 点击鼠标光标变成十字状态 出现截屏窗口调整截屏窗口位置与大小 释放鼠标,三个btn,,  复制 ...

  8. Atitit apache 和guava的反射工具

    Atitit apache 和guava的反射工具 apache1 Spring的反射工具类 ReflectionUtils1 Guava 反射工具2 apache  34             7 ...

  9. Atitit intellij idea的使用总结attilax

    Atitit intellij idea的使用总结attilax 1. ideaIC-2016.2.4.exe1 1.1. Ij vs eclipse市场份额1 1.2. Ij的优点(方便的支持gro ...

  10. Jenkins的插件管理(安装和更新插件)

    使用Jenkins的编译部署项目需要依赖各种插件 下面安装Jenkins的各种插件: 1.登录Jenkins进入以下界面: 2.点击 系统管理 : 3.点击 管理插件 : 4.点击 可选插件 选择你需 ...