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) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM