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文件,只需要在代碼函數里面動態添加幾個表之間的關系即可。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容