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文件,只需要在代码函数里面动态添加几个表之间的关系即可。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容