軟體開發(軟件開發)

網智數位主要提供套裝及客製化的軟體系統解決方案,專為客戶量身訂做客製化的軟體,達成客製化、智慧化及網路化的管理功能。

室內設計、裝潢、窗簾報價估算軟體

網智數位主要提供套裝及客製化的軟體系統解決方案,針對室內設計師、木工、裝潢業產業,量身訂做客製化的軟體,達成客製化、智慧化及網路化的商用軟體。

商用軟體-客製化設計

網智數位主要提供套裝及客製化的軟體系統解決方案,專為客戶量身訂做客製化的軟體,達成客製化、智慧化及網路化的管理功能。

IOT 物聯網-系統開發

根據客戶實際狀況,結合雲端與載具進行客製化物聯網IOT導入與軟體開發

雲端VPS虛擬主機租用

我們的雲端VPS虛擬主機是採用雲端(虛擬化)技術所開發之全新雲端伺服器服務,可以選擇多種作業系統(Windows、Linux等),客戶可載入自訂的應用環境,執行自己所要提供的網路服務,我們的雲端服務可為您的網站提供最完美的解決方案。

ERP軟體客製化導入

ERP軟體客製化導入,室內設計、營造業、裝潢、木作工程、系統櫃工程、會計系統,全面提升公司管理營運效率。

搜尋引擎最佳化SEO

搜尋引擎最佳化(SEO)不僅能提高網站在搜尋結果的排名,更能帶來大量對我們產品或服務真正有需求的訪客。SEO 最棒的特質之一就是不像廣告一樣亂槍打鳥而導致用戶的反感,反而更能提升點閱率跟成交率喔。

服務宗旨

網智數位主要提供套裝及客製化的軟體系統解決方案,專為客戶量身訂做客製化的軟體,達成客製化、智慧化及網路化的管理功能。

我們的成立宗旨就是要以最猛的IT技術讓這個世界更Smart,在我們貫徹我們裡想的同時,我們希望可以把我們所開發的系統帶給台灣的中小企業,除了要推薦好的東西之外,我們也希望做點改變,所以我們的第一目標就是要使用最好用的系統再加上您寶貴的創意,不僅僅可以節省你大量的荷包,還可以有一個像樣的網站。我們可以幫你做的有

企業管理
  • 策略管理
  • 目標管理
  • 行銷管理
  • 財會管理
  • ERP導入
  • 企業流程自訂
資訊管理
  • 網站架設
  • 虛擬化/雲端架設
  • 主機代管
  • 私有雲建制與導入
軟體開發
  • UML設計
  • 版本控管
  • 企業軟體開發
  • APP開發
  • 網頁設計
資訊安全
  • 網頁弱點掃描
  • 主機弱點掃描
  • 木馬檢測
  • 資安鑑識
  • 設計網路架構
  • 資安監控
行銷
  • 關鍵字SEO
  • 社群網路行銷
  • 部落格行銷
  • FaceBook 粉絲團
其他
  • 協助企業申請Google Email
好玩工具開發

講出你的創意吧!沒有甚麼是資訊辦不到的

2014年11月9日 星期日

[Entity Framework] 效能深入探討(一)

      作者我在使用Entity Framewok開發時,最常聽到的一句話就是,Entity Framework 是否"好像"效能很慢喔,在微軟剛推出Entity Framewok前(事實上微軟真的這方面算是進展很慢,其他程式陣營早就有相關的Framework,例如 Java 的 Hibernate,不過後來真的速度改版的很迅速,微軟都這樣玩法),我會很肯定的回答"是",如果當時要採用 Entity Framewok時,我想可能最大的益處就是可以在程式開發過程時,比較可以使用"概念模型"(Conceptual Model)以及 OOP(物件導向)的思維導向,也可以避免程式開發人員在寫程式盡可能避免了 SQL Script,當然也避免了一些開發人員真的不太會寫 SQL,相信我真的還遇到過,不過隨著Entity Framewok 發展到 Version 6 UP.., 我相信大多數是可以依靠 EF(Entity Framewok),而且開發速度的優勢已經是大多數優先考慮採用的原因,尤其現在到處都是 WEB API、MVC架構(EF歸於 Model  Layer),我們更需要好好學習一下,所以我打算寫幾篇關於 EF效能的底層技術與原理,畢竟 Entity Framewok 雖然把底層 SQL的工作,都幫你處理好,但你知道為何有時似乎效能真的好像還是很慢,所以我想直接跟讀者說一些原理,這也是市面上書籍似乎都沒在探討的.....

     我先在此介紹一個Entity Framework 的術語 Lazy Load , 它代表 延遲載入 預設值是成立(true)  。為了解釋何謂Lazy Load,我自己帶入一個案例來進行解釋
例如在我們 CRM系統裏有 [客戶類型]類別 (CustCate Class) , 而每個[客戶類型]可以有多個興趣的[客戶]類別 (Customer Class),此時很典型存在所謂的一對多 One-To-Many 的模型 ;而每個[客戶]類別也可以記錄此客戶相關的[客戶事件]類別(CustomerEvent Class)
,下面我列出了案例模型圖


根據上述模型我們來實作一個 Code First EF Model 
實作 Customer 、CustCate、CustomerEvent Class Code ....

    [Table("Customer")]
    public partial class Customer
    {
        public Customer()
        {
            CustomerEvent = new HashSet<CustomerEvent>();
        }

        [Key]
        [StringLength(15)]
        public string No { get; set; }

        [StringLength(10)]
        public string CName { get; set; }

        [StringLength(10)]
        public string ENmae { get; set; }

        [StringLength(10)]
        public string CateCode { get; set; }

        public virtual CustomerCate CustomerCate { get; set; }

        public virtual ICollection<CustomerEvent> CustomerEvent { get; set; }
    }

    
    [Table("CustomerCate")]
    public partial class CustomerCate
    {
        public CustomerCate()
        {
            Customer = new HashSet<Customer>();
        }

        [Key]
        [StringLength(10)]
        public string Code { get; set; }

        [Required]
        [StringLength(15)]
        public string Name { get; set; }

        public virtual ICollection<Customer> Customer { get; set; }
    }

    [Table("CustomerEvent")]
    public partial class CustomerEvent
    {
        [Key]
        [StringLength(36)]
        public string EventID { get; set; }

        [StringLength(15)]
        public string CustNo { get; set; }

        [Required]
        [StringLength(300)]
        public string Desc { get; set; }

        public DateTime CreateDate { get; set; }

        public virtual Customer Customer { get; set; }
    }

     
實作一個繼承 DbContext 的 EF 存取容器

    public class CRMEF : DbContext
    {
        public CRMEF()
            : base("name=EF")
        {
        }

        public virtual DbSet<Customer> Customer { get; set; }
        public virtual DbSet<CustomerCate> CustomerCate { get; set; }
        public virtual DbSet<CustomerEvent> CustomerEvent { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Customer>().ToTable("Customer");
            modelBuilder.Entity<CustomerCate>().ToTable("CustomerCate");
            modelBuilder.Entity<CustomerEvent>().ToTable("CustomerEvent");

            modelBuilder.Entity<Customer>().HasMany(e => e.CustomerEvent)
                                           .WithOptional(e => e.Customer)
                                           .HasForeignKey(e => e.CustNo);

            modelBuilder.Entity<CustomerCate>().HasMany(e => e.Customer)
                                               .WithOptional(e => e.CustomerCate)
                                               .HasForeignKey(e => e.CateCode);

            base.OnModelCreating(modelBuilder);
        }
    }


我也設計一個前端界面

在寫入測試資料 Button,先寫入基本資料以利後面的解說
private void btnTestData_Click(object sender, EventArgs e)
{
  try
   {
       using (CRMEF ef = new CRMEF())
        {
             CustomerCate cate1 = new CustomerCate() { Code = "Retail", Name = "零售商" };
             CustomerCate cate2 = new CustomerCate() { Code = "Direct", Name = "直客" };
             CustomerCate cate3 = new CustomerCate() { Code = "EC", Name = "網路消費者" };

             Customer cust1 = new Customer() { CustomerCate = cate1, No = "001", CName = "智慧零售商", ENmae = "Smart" };
             CustomerEvent custEvent1 = new CustomerEvent() { Customer = cust1, EventID = Guid.NewGuid().ToString(), Desc = "詢問新產品目錄", CreateDate = DateTime.Now };
             ef.CustomerEvent.Add(custEvent1);
             CustomerEvent custEvent2 = new CustomerEvent() { Customer = cust1, EventID = Guid.NewGuid().ToString(), Desc = "購買10套 網智數位 CRM 套裝軟體", CreateDate = DateTime.Now };
             ef.CustomerEvent.Add(custEvent2);
             Customer cust2 = new Customer() { CustomerCate = cate2, No = "002", CName = "柯P", ENmae = "柯P" };
             CustomerEvent custEvent3 = new CustomerEvent() { Customer = cust2, EventID = Guid.NewGuid().ToString(), Desc = "尋找物聯網的合作廠商", CreateDate = DateTime.Now };
             ef.CustomerEvent.Add(custEvent3);
             Customer cust3 = new Customer() { CustomerCate = cate3, No = "003", CName = "連勝吻", ENmae = "連勝吻" };
             CustomerEvent custEvent4 = new CustomerEvent() { Customer = cust3, EventID = Guid.NewGuid().ToString(), Desc = "網路行銷推廣", CreateDate = DateTime.Now };
             ef.CustomerEvent.Add(custEvent4);

             ef.SaveChanges();
             MessageBox.Show("Test Insert OK");
         }
    }
    catch { }
 }

執行後,我們可以看到後端資料庫就存在一下資料


現在有了案例的資料後,我就可以開始解釋何謂 Lazy Loading,在上述案例我們如果需要列出所有客戶以及他相關的活動事件,以及此客戶歸屬的類別,我把它寫在 "Lazy Loading" Button Click:

  1. private void btnLazyLoading_Click(object sender, EventArgs e)
  2. {
  3. this.richTextBox1.Clear();
  4. this.richTextBox1.AppendText("使用 Lazy Loading 讀取,系統預設行為! \r\n");
  5. this.richTextBox1.AppendText("------------------------------------------------------------------------- \r\n");

  6. using (CRMEF ef = new CRMEF())
  7. {
  8. var customers = ef.Customer;

  9. foreach (var cust in customers)
  10. {
  11. this.richTextBox1.AppendText("客戶編號 : " + cust.No + " 客戶姓名 :" + cust.ENmae + "\t  類型 :" + cust.CustomerCate.Name + "\r\n");
  12. this.richTextBox1.AppendText("--------------------------------事件記錄------------------------------------ \r\n");

  13. foreach (var evt in cust.CustomerEvent)
  14. {
  15. this.richTextBox1.AppendText(evt.Desc + "\r\n");
  16. }

  17. this.richTextBox1.AppendText("\r\r\n");
  18. }
  19. }
  20. }
執行後結果如下

看樣子結果都是沒錯的,但各位我要告訴你們,你知道 上述結果 Entity Framewok 是幫我們產生多少次 SQL語法嗎? , 以及來回跟後端資料庫查詢幾次嗎?
答案是 7 次 ,也就是說白話一點,就是 EF要來回跟後端資料庫溝通要得資料7次,才可以把我們要呈現的資料,完全取回來,各位如果客戶資料不是現在案例的3筆,而100筆...1000筆....甚至 10000筆以上呢?

我分別把我追蹤後的SQL 語法寫下:
第 1 次 SQL :
SELECT
    [Extent1].[No] AS [No],
    [Extent1].[CName] AS [CName],
    [Extent1].[ENmae] AS [ENmae],
    [Extent1].[CateCode] AS [CateCode]
    FROM [dbo].[Customer] AS [Extent1]

第 2 次 SQL :
exec sp_executesql N'SELECT
    [Extent1].[Code] AS [Code],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[CustomerCate] AS [Extent1]
    WHERE [Extent1].[Code] = @EntityKeyValue1',N'@EntityKeyValue1 nvarchar(10)',@EntityKeyValue1=N'Retail'

第 3 次 SQL :
exec sp_executesql N'SELECT
    [Extent1].[EventID] AS [EventID],
    [Extent1].[CustNo] AS [CustNo],
    [Extent1].[Desc] AS [Desc],
    [Extent1].[CreateDate] AS [CreateDate]
    FROM [dbo].[CustomerEvent] AS [Extent1]
    WHERE [Extent1].[CustNo] = @EntityKeyValue1',N'@EntityKeyValue1 nvarchar(15)',@EntityKeyValue1=N'001'

第 4 次 SQL :
exec sp_executesql N'SELECT
    [Extent1].[Code] AS [Code],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[CustomerCate] AS [Extent1]
    WHERE [Extent1].[Code] = @EntityKeyValue1',N'@EntityKeyValue1 nvarchar(10)',@EntityKeyValue1=N'Direct'

第 5 次 SQL :
exec sp_executesql N'SELECT
    [Extent1].[EventID] AS [EventID],
    [Extent1].[CustNo] AS [CustNo],
    [Extent1].[Desc] AS [Desc],
    [Extent1].[CreateDate] AS [CreateDate]
    FROM [dbo].[CustomerEvent] AS [Extent1]
    WHERE [Extent1].[CustNo] = @EntityKeyValue1',N'@EntityKeyValue1 nvarchar(15)',@EntityKeyValue1=N'002'

第 6 次 SQL :
exec sp_executesql N'SELECT
    [Extent1].[Code] AS [Code],
    [Extent1].[Name] AS [Name]
    FROM [dbo].[CustomerCate] AS [Extent1]
    WHERE [Extent1].[Code] = @EntityKeyValue1',N'@EntityKeyValue1 nvarchar(10)',@EntityKeyValue1=N'EC'

第 7 次 SQL :
exec sp_executesql N'SELECT
    [Extent1].[EventID] AS [EventID],
    [Extent1].[CustNo] AS [CustNo],
    [Extent1].[Desc] AS [Desc],
    [Extent1].[CreateDate] AS [CreateDate]
    FROM [dbo].[CustomerEvent] AS [Extent1]
    WHERE [Extent1].[CustNo] = @EntityKeyValue1',N'@EntityKeyValue1 nvarchar(15)',@EntityKeyValue1=N'003'


這就是 Entitfy Framework 的預設行為 Lazy Loading , 上述 C# 語法我們先列舉了所有客戶的清單,此時 EF 就會產生 第一個 SQL 語法 是讀取後端 [dbo].[Customer] Table 的 SQL ,
而我們再依序根據每筆客戶分別讀取了客戶類型,此時 EF 又會去跟後端資料庫要求讀取 [dbo].[CustomerCate] Table ,所以有幾筆客戶資料,他就會來回跟資料庫讀取 [dbo].[CustomerCate] Table,各位這就是效能瓶頸所在
接下來我們繼續讀取每筆客戶的系統事件,此時 EF 又要懶惰 (Lazy Loading)的再去跟後端資料庫取得  [dbo].[CustomerEvent] 資料表,看吧!,系統如果資料量大一點,這樣大部分人使用 EF (Entitfy Framework) ,一定死了,被客戶抱怨死了,
此時通常工程師或開發人員就會抱怨 Entity Framewok好爛,效能好差,還是用ADO.Net好....這也是我公司工程師常跟我說類似的話,但如果你知道底層的原理,這都是可以操控自如的,完全可以任意操控EF,使它可以不用笨到來來回回奔走,
至於如何做呢? 就讓我留著下一回好好地告訴大家。。。。

2014年11月2日 星期日

【EntityFramework 實戰】 一對多關聯

     這篇教學文件,我想一步一步的教讀者如何利用 EntityFramework 來寫一對多 (One-To-Many) 關聯 Table的程式碼技巧,並且分別利用 Entity SQL
LINQ To SQL  的方式來讀取資料庫。

在實例個案,我們常常會遇到如下的資料表一對多關聯

(SQL 語法)
  1. CREATE TABLE Employee
  2. (
  3.   [No] NVARCHAR( 12) NOT NULL PRIMARY KEY,
  4.   HireDate Datetime not null default(getdate ()),
  5.   Name NVARCHAR( 12) NOT NULL
  6. )

  7. CREATE TABLE EmployeeSalary
  8. (
  9.  EmpNo NVARCHAR(12) NOT NULL,
  10.  PayYear INT NOT NULL,
  11.  PayMonth INT NOT NULL,
  12.  bonus DECIMAL(18,2) NOT NULL,
  13.  constraint PK_EmployeeSalary primary key (EmpNo,PayYear,PayMonth),
  14.  constraint FK_EmployeeSalary foreign key (EmpNo)
  15.       references Employee ([No])
  16. )


          在上述案例,我們可以知道一個員工(Employee)在每年的每個月有可能因為績效達到某些企業設定的條件,因此可以得到應領取的獎金(EmployeeSalary) , 但也有可能某員工為新進員工,所以雖然在 Employee 資料表存在記錄,但 EmplyeeSalary 卻不存在任何記錄,如果此時企業的系統應用要求,列出所有員工(不論是否已有任何獎金),如有獎金也顯示出在前端界面,我們該如何利用 EntityFramework 去實戰這個很普遍到不行的案例呢? 下面我就示範出比較適當的程式技巧。

首先在此我一樣是利用 Code-First Style 來操控 EntityFramework , 並前端界面我利用Windows Form來示範,前端界面圖示如下

在寫入資料 Button ,我先示範利用 EntityFramewok,來寫入所需的測試資料

     1:    private void btnInsert_Click(object sender, EventArgs e)  
     2:          {  
     3:              using (DBEntities entities = new DBEntities())  
     4:              {  
     5:                  Employee emp1 = new Employee()  
     6:                                  {  
     7:                                      No = "001", //員工編號  
     8:                                      HireDate = DateTime.Parse("2014/05/01"), //僱用日期  
     9:                                      Name = "湯姆克魯斯", //員工姓名                                       
    10:                                  };  
    11:                  EmployeeSalary salary1 = new EmployeeSalary()  
    12:                                           {  
    13:                                               bonus = 1000, //獎金  
    14:                                               PayYear = 2014, //年  
    15:                                               PayMonth = 9, //月                                                
    16:                                           };  
    17:                  emp1.EmployeeSalary.Add(salary1);  
    18:     
    19:                  Employee emp2 = new Employee()  
    20:                  {  
    21:                      No = "002", //員工編號  
    22:                      HireDate = DateTime.Parse("2014/06/01"), //僱用日期  
    23:                      Name = "李奧納多", //員工姓名                                       
    24:                  };  
    25:                  EmployeeSalary salary2_1 = new EmployeeSalary()  
    26:                  {  
    27:                      bonus = 1000, //獎金  
    28:                      PayYear = 2014, //年  
    29:                      PayMonth = 9, //月                                                
    30:                  };  
    31:                  emp2.EmployeeSalary.Add(salary2_1);  
    32:                  EmployeeSalary salary2_2 = new EmployeeSalary()  
    33:                  {  
    34:                      bonus = 6000, //獎金  
    35:                      PayYear = 2014, //年  
    36:                      PayMonth = 10, //月                                                
    37:                  };  
    38:                  emp2.EmployeeSalary.Add(salary2_2);  
    39:     
    40:     
    41:                  Employee emp3 = new Employee()  
    42:                  {  
    43:                      No = "003", //員工編號  
    44:                      HireDate = DateTime.Parse("2014/10/01"), //僱用日期  
    45:                      Name = "基諾李維", //員工姓名                                       
    46:                  };  
    47:     
    48:                  entities.Employee.Add(emp1);  
    49:                  entities.Employee.Add(emp2);  
    50:                  entities.Employee.Add(emp3);  
    51:     
    52:                  entities.SaveChanges();  
    53:              }  
    54:          }  

執行寫入資料按鈕後,後端資料庫結果為如下
 

測試資料有了以後,開始我們可以分別利用 Entity SQL 與 LINQ To Entities 兩種技巧來讀取

在 顯示資料(Entity SQL) 按鈕 ,程式如下
  
     1:   private void button1_Click(object sender, EventArgs e)  
     2:          {  
     3:              this.richTextBox1.Clear();  
     4:     
     5:              using(DBEntities entites = new DBEntities())  
     6:              {  
     7:                  var eSQL = @"SELECT A.No,A.Name,A.HireDate,B.PayYear,B.PayMonth,B.bonus  
     8:                                FROM Employee AS A OUTER APPLY A.EmployeeSalary AS B   
     9:                                ORDER BY A.No ";  
    10:                  var allEmpSalaryRecords = ((IObjectContextAdapter)entites).ObjectContext.CreateQuery<DbDataRecord>(eSQL).ToList();  
    11:     
    12:                  this.richTextBox1.AppendText("所有員工與獎金明細表:\r\n");  
    13:                  this.richTextBox1.AppendText("-------------------------------------------------------------------\r\n");  
    14:     
    15:                  //開始讀取  
    16:                  foreach(var empRecord in allEmpSalaryRecords)  
    17:                  {  
    18:                      if(empRecord["bonus"] != DBNull.Value)  
    19:                      {  
    20:                          this.richTextBox1.AppendText( "員工姓名 : " + empRecord["Name"].ToString() + " \r\n");  
    21:                          this.richTextBox1.AppendText("(年/月)  " + empRecord["PayYear"].ToString() + "/" + empRecord["PayMonth"].ToString()   
                                                                           + " 獎金為 " + empRecord["bonus"].ToString() + " \r\n");  
    22:     
    23:                      }  
    24:                      else  
    25:                      {  
    26:                          this.richTextBox1.AppendText("員工姓名 : " + empRecord["Name"].ToString() + " \r\n");  
    27:                          this.richTextBox1.AppendText(" 未有獎金 \r\n");  
    28:                      }  
    29:                  }  
    30:     
    31:              }  
    32:          }  

執行結果如下


在 顯示資料(LINQ) 按鈕 ,程式如下
  1. private void button2_Click(object sender, EventArgs e)
  2. {
  3.      this.richTextBox1.Clear();

  4.      using (DBEntities entities = new DBEntities())
  5.      {
  6.           var allEmpSalaryRecords = (from emp in entities.Employee
  7.                                            from empSalary in emp.EmployeeSalary.DefaultIfEmpty()
  8.                                            orderby emp.No
  9.                                            select new
  10.                                            {
  11.                                                 emp.Name,
  12.                                                 PayYear = (int?)empSalary.PayYear,
  13.                                                 PayMonth = (int?)empSalary.PayMonth,
  14.                                                 Bonus = (decimal?)empSalary.bonus
  15.                                            }).ToList();

  16.           //開始讀取
  17.           foreach (var empRecord in allEmpSalaryRecords)
  18.           {
  19.                if (empRecord.Bonus.HasValue)
  20.                {
  21.                     this.richTextBox1.AppendText("員工姓名 : " + empRecord.Name + " \r\n");
  22.                     this.richTextBox1.AppendText("(年/月)  " + empRecord.PayYear.ToString() + "/" + empRecord.PayMonth.ToString() 
  23.                                                                          + " 獎金為 " + empRecord.Bonus.ToString() + " \r\n");

  24.                }
  25.                else
  26.                {
  27.                     this.richTextBox1.AppendText("員工姓名 : " + empRecord.Name + " \r\n");
  28.                     this.richTextBox1.AppendText(" 未有獎金 \r\n");
  29.                }
  30.           }
  31.      }
  32. }
執行結果


透過以上2種讀取技巧,我追蹤EntityFramework 6都是自動產生以下 SQL 語法
  1. SELECT 
  2.     [Project1].[C1] AS [C1], 
  3.     [Project1].[No] AS [No], 
  4.     [Project1].[Name] AS [Name], 
  5.     [Project1].[HireDate] AS [HireDate], 
  6.     [Project1].[PayYear] AS [PayYear], 
  7.     [Project1].[PayMonth] AS [PayMonth], 
  8.     [Project1].[bonus] AS [bonus]
  9.     FROM ( SELECT 
  10.         [Extent1].[No] AS [No], 
  11.         [Extent1].[HireDate] AS [HireDate], 
  12.         [Extent1].[Name] AS [Name], 
  13.         [Extent2].[PayYear] AS [PayYear], 
  14.         [Extent2].[PayMonth] AS [PayMonth], 
  15.         [Extent2].[bonus] AS [bonus], 
  16.         1 AS [C1]
  17.         FROM  [dbo].[Employee] AS [Extent1]
  18.         LEFT OUTER JOIN [dbo].[EmployeeSalary] AS [Extent2] ON [Extent1].[No] = [Extent2].[EmpNo]
  19.     )  AS [Project1]
  20.     ORDER BY [Project1].[No] ASC

以上 One-To-Many的EntityFramework 6 讀取方式,完整介紹完畢,希望對讀取有所幫忙

2014年10月29日 星期三

Entity Framework 實戰 - Table Per Hierarchy Inheritance (TPH)實作

      今天我想抽空來寫篇關於 .Net EntityFramework 與 資料庫單一資料表(Table)的實作,這次實作機制方法為 Table Per Hierarchy Inheritance(TPH),顧名思義就是在底層資料庫的一個資料表對應了程式的資料模型多個實體,
我直接舉個例子來解釋資料庫與領域模型間的圖型。

資料庫 Table 圖例

然而在系統設計時,領域模型我們採取了一下設計


在上圖系統領域模型中, Media 為父類別 (Parent Class) ,而 Article 與 Video、Photo皆為繼承 Media 父類別的子類別(Child Class)


PS.注意,在 Media 類別,並沒有 Media 資料表的欄位 MediaType , 而 MediaType欄位的值在資料表是用來識別被繼承的子類別是屬於那個型別,
如 MediaType = "Article" , 此時代表為 Article Class ,依次為 MediaType = "Video" 代表為 Video Class ; MediaType = "Photo" 代表為 Photo Class 。

現在我開始撰寫 Media 、Article 、Video、Photo Class Code


因為 我採用 Code-First 的方式去實作 EntityFramework ,所以底下我設計一個 DBEntities Class ,並讓它繼承了 DbContext ,
這樣後續我就可以透過 DBEntitis 去處理後續的實體增修 CRUD。

上述步驟都完成後,接下來可以來測試透過 DBEntities 來進行新增并實際寫入資料表。
在這邊我利用 Windows Form 當做前端界面來實作


而在 寫入(存檔)的  Button  ,我寫了以下程式
    1:          private void button1_Click(object sender, EventArgs e)  
    2:         {  
    3:             //進行存檔
    4:             using(var context = new DBEntities())  
    5:             {  
    6:                 Article article = new Article() { Title = "網智數位行銷手冊秘笈", MediaID = "001" };  
    7:                 context.Media.Add(article);  
    8:   
    9:                 Video video = new Video() { Title = "網智ERP教學影片", MediaID = "002" };  
   10:                 context.Media.Add(video);  
   11:   
   12:                 Photo photo = new Photo() { Title = "網智數位設計", MediaID = "003" };  
   13:                 context.Media.Add(photo);  
   14:   
   15:                 context.SaveChanges();  
   16:   
   17:             }  
   18:         }  

點擊 寫入(存檔)Button後,此時在去後端資料庫檢查,我們可以看到結果如下

此時,資料表正確無誤的寫入 3 筆資料列,並且 MediaType 欄位,也會自動寫入對應的值(Article、Video、Photo)。




網智數位-軟體開發