軟體開發(軟件開發)

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

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

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

商用軟體-客製化設計

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

IOT 物聯網-系統開發

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

雲端VPS虛擬主機租用

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

ERP軟體客製化導入

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

搜尋引擎最佳化SEO

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

服務宗旨

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

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

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

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

顯示具有 Entity Framework 標籤的文章。 顯示所有文章
顯示具有 Entity Framework 標籤的文章。 顯示所有文章

2015年11月28日 星期六

Entity Framework 實戰 - 多對多自我關聯 Many to Many , Self Referencing 模型實作 (二)

此篇文是 Entity Framework 實戰 - 多對多自我關聯 Many to Many , Self Referencing 模型實作 (一)的續篇 , 當我們把基本模型 Product Class 建立好後,我們開始實作一個繼承 DbContext 的子類別,在這邊你可以把 DbContext 當做是資料庫的層次意義角度來看待
新增一個Class 名為 ProductContext.cs
image
然後按【新增】按鈕,並在 ProductContext 宣示繼承 DbContext 類別,如以下程式碼
  1: using System;
  2: using System.Collections.Generic;
  3: using System.Data.Entity;
  4: using System.Linq;
  5: using System.Text;
  6: using System.Threading.Tasks;
  7: 
  8: namespace EF6_ManyToManyAndSelf
  9: {
 10:     public class ProductContext : DbContext
 11:     {
 12: 
 13:     }
 14: }

接著加入 DbSet<Product> 的宣告,用於表示 存放著 Product Table(Class)的集合清單
public DbSet<Product> Products;
然後為了完成透過 Product 本身的 Class 來完成 多對多自身關聯,需要 override OnModelCreating Method , 程式碼如下

   1:   protected override void OnModelCreating(DbModelBuilder modelBuilder)
   2:          {
   3:              base.OnModelCreating(modelBuilder);
   4:   
   5:              modelBuilder.Entity<Product>()
   6:                          .HasMany(p => p.RelatedProducts)
   7:                          .WithMany(p => p.AboveProducts)
   8:                          .Map(m =>
   9:                                    {
  10:                                        m.MapLeftKey("ProductID");
  11:                                        m.MapRightKey("RelatedProductID");
  12:                                        m.ToTable("RelatedProduct");
  13:                                    }
  14:                          );
  15:          }

到這邊基本上已經完成 EntityFramework 的宣告….

然後讓我們在前端界面使用我們實作好的 ProductContext ,來看看如何使用….程式碼如下


   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Threading.Tasks;
   6:   
   7:  namespace EF6_ManyToManyAndSelf
   8:  {
   9:      class Program
  10:      {
  11:          static void Main(string[] args)
  12:          {
  13:              using (var context = new ProductContext())
  14:              {
  15:                  var product1 = new Product { ProductName = "美容業零售管理系統", Price = 59888M };
  16:                  var product2 = new Product { ProductName = "窗簾業ERP系統", Price = 3500000M };
  17:                  var product3 = new Product { ProductName = "玩具製造業ERP系統", Price = 6700000M };
  18:                  product2.RelatedProducts.Add(product3);
  19:                  product1.RelatedProducts.Add(product2);
  20:                  context.Products.Add(product1);
  21:                  context.SaveChanges();
  22:              }
  23:              using (var context = new ProductContext())
  24:              {
  25:                  var product2 = context.Products.First(p => p.ProductName == "窗簾業ERP系統");
  26:                  Console.WriteLine("Product: {0} ... {1}", product2.ProductName, product2.Price.ToString("C"));
  27:                  Console.WriteLine("Related Products");
  28:                  foreach (var prod in product2.RelatedProducts)
  29:                  {
  30:                      Console.WriteLine("\t{0} ... {1}", prod.ProductName, prod.Price.ToString("C"));
  31:                  }
  32:                  foreach (var prod in product2.AboveProducts)
  33:                  {
  34:                      Console.WriteLine("\t{0} ... {1}", prod.ProductName, prod.Price.ToString("C"));
  35:                  }
  36:              }
  37:   
  38:              Console.ReadKey();
  39:          }
  40:      }
  41:  }


 
 



執行結果為


image





網智數位-軟體開發(軟件開發)

Entity Framework 實戰 - 多對多自我關聯 Many to Many , Self Referencing 模型實作 (一)

在我們軟體開發過程中,我們會遇到一種特殊模型實體關係,例如一個線上商品系統,會有產品資料表(Product Table),以及記錄產品的【相關產品】和該產品本身可以被關聯的上層【相關產品】,感覺很繞口,下圖我截取一個 ER-D 示例圖:
image
上圖ERD 關聯存在所謂的多對多關係 (Many to Many)而且又是自我關聯(自身關聯,Self Referencing),本身的主要邏輯是 RelatedProduct 這個Table,擔任一個用來記錄多對多關聯記錄的中繼Table,RelatedProduct Table包含了2個欄位(ProductID、RelatedProductID),2個欄位都是參考(Referencing)到 Product Table的 ProductID 主鍵值 ( Primary Key ),透過這樣的技巧可以表達一個產品可以關聯多個相關產品,反之也可以表達一個產品可以被多少產品有所關聯….
但現在我要教大家實作一個只需要在 Entity Framework 建立一個 Product Model (POCO),然後透過一些程式寫法技巧,來達成 這種商業資料模型  - 》 多對多自我關聯 Many to Many , Self Referencing 。
首先讓我們用 Visual Studio 開發工具來先新增一個程式專案,在此我程式專案命名為 EF6_ManyToManyAndSelf,然後按確定。
image
接下來,我們需要透過 NuGet來安裝 EntityFramework ,
image
image
在上圖執行安裝按鈕後,後續 NuGet 就會開始安裝 EntityFramewok , 目前 Allen 裝的版本為 6.1.3 穩定版,安裝過程可能會等候一點點時間,裝完後,你會發現專案的參考對了2個參考項目
image
確定沒問題後,接下來我們來新增一個 Product Class ,用來代表 Product table 的對應,完整程式碼如下:
image
   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.ComponentModel.DataAnnotations.Schema;
   5:  using System.Linq;
   6:  using System.Text;
   7:  using System.Threading.Tasks;
   8:   
   9:  namespace EF6_ManyToManyAndSelf
  10:  {
  11:      [Table("Product", Schema = "dbo")]
  12:      public class Product
  13:      {
  14:          public Product()
  15:          {
  16:              RelatedProducts = new HashSet<Product>();
  17:              AboveProducts = new HashSet<Product>();
  18:          }
  19:   
  20:          [Key]
  21:          [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  22:          public int ProductID { get; set; }
  23:   
  24:          public string ProductName { get; set; }
  25:   
  26:          public decimal Price { get; set; }
  27:   
  28:          /// <summary>
  29:          /// 記錄此產品的多個相關的產品
  30:          /// </summary>
  31:          public virtual ICollection<Product> RelatedProducts { get; set; }
  32:   
  33:          /// <summary>
  34:          /// 記錄此產品被那些上層產品標示為相關的產品
  35:          /// </summary>
  36:          public virtual ICollection<Product> AboveProducts { get; set; }
  37:   
  38:      }
  39:  }
 

    實作到這邊已經把基本的 EntityFramework 安裝進來,也把產品模型寫成一個POCO Class ,後續是更進階的 程式技巧已經背後運作解釋,請繼續參閱我撰寫  Entity Framework 實戰 - 多對多自我關聯 Many to Many , Self Referencing 模型實作 (二) 的介紹。



網智數位-軟體開發 (軟件開發)

2014年11月12日 星期三

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

     在上一篇我花了一些時間談到,當我們載入資料庫模型是屬於 1 對多 (One-To-Many)時,預設值 Entity Framework 6 是利用一個稱為 Lazy Loading 機制,也就是當需要一個資料的其他相關(關聯)資料時,EF 才會產生SQL語法去跟後端資料庫要求相關資料,而資料應用程式效能的殺手之一,就是太繁複去跟後端資料庫來回存取,為了克服 EF 預設行為的效能問題,我想來介紹一個 EF 另外一個機制術語,稱為 Eager Loading ....


 

     使用 Eager Loading 機制,主要可以在一開始讀取資料時,例如讀取 Customer Table 時,如果我們知道後續一定會讀取相關聯的資料表(類別),如案例的 CustomerCate Table、CustomerEvent Table,那麼我們就可以在第一次讀取 Customer Table 時就一次讀取所有相關的資料表,這樣後續應用程式需要相關資料時,只要在記憶體立即讀取,而不是需要用到時,才延遲跟資料庫索取相關資料,解釋那麼多,到底程式語法該如何實作呢?我們會用到一個方法(Method)為 Include ....

    PS.這次實作我完全根據 上一篇[Entity Framework] 效能深入探討(一) 繼續介紹,前端我暫時利用 Windows Form 來簡單的 Demo
,然後我會把焦點放在 Include Loading(1) 與  Include Loading(2) 的 程式語法
 

在  Include Loading(1) Button Click事件,我撰寫了一些程式碼:

  1. this.richTextBox1.Clear();
  2. this.richTextBox1.AppendText("使用 Eager Loading 機制 Include Method ! \r\n");
  3. this.richTextBox1.AppendText("------------------------------------------------------------------------- \r\n");

  4. using (CRMEF ef = new CRMEF())
  5. {
  6.      var customers = ef.Customer.Include("CustomerCate").Include("CustomerEvent");

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

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

  15.           this.richTextBox1.AppendText("\r\r\n");
  16.      }
  17. }

     注意上述程式碼的第 7 行我使用了2次 Include 方法,並在該方法傳遞是關聯類型的字串,透過這樣的方式,但程式第一次需要讀取 Customer 資料表時,
Entity Framewok(EF)就會自動產生的 SQL語法是把 Customer、CustomerCate、CustomerEvent 3個 Table,一次完全跟後端資料庫的索取,並記錄到記憶體(DataContext Cache),所以整個 SQL 傳輸只有 1 次,而不是上一篇介紹的 7 次哦(效能差很大,這也是一般書上很少寫的)。

底下我列出我追蹤的 SQL 語法如下
  1. SELECT
  2.     [Project1] .[C1] AS [C1],
  3.     [Project1] .[No] AS [No],
  4.     [Project1] .[CName] AS [CName],
  5.     [Project1] .[ENmae] AS [ENmae],
  6.     [Project1] .[CateCode] AS [CateCode],
  7.     [Project1] .[Code] AS [Code],
  8.     [Project1] .[Name] AS [Name],
  9.     [Project1] .[C2] AS [C2],
  10.     [Project1] .[EventID] AS [EventID],
  11.     [Project1] .[CustNo] AS [CustNo],
  12.     [Project1] .[Desc] AS [Desc],
  13.     [Project1] .[CreateDate] AS [CreateDate]
  14.      FROM ( SELECT
  15.         [Extent1] .[No] AS [No],
  16.         [Extent1] .[CName] AS [CName],
  17.         [Extent1] .[ENmae] AS [ENmae],
  18.         [Extent1] .[CateCode] AS [CateCode],
  19.         [Extent2] .[Code] AS [Code],
  20.         [Extent2] .[Name] AS [Name],
  21.         1 AS [C1],
  22.         [Extent3] .[EventID] AS [EventID],
  23.         [Extent3] .[CustNo] AS [CustNo],
  24.         [Extent3] .[Desc] AS [Desc],
  25.         [Extent3] .[CreateDate] AS [CreateDate],
  26.          CASE WHEN ([Extent3]. [EventID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
  27.          FROM   [dbo]. [Customer] AS [Extent1]
  28.          LEFT OUTER JOIN [dbo] .[CustomerCate] AS [Extent2] ON [Extent1] .[CateCode] = [Extent2].[Code]
  29.          LEFT OUTER JOIN [dbo] .[CustomerEvent] AS [Extent3] ON [Extent1] .[No] = [Extent3].[CustNo]
  30.      )  AS [Project1]
  31.      ORDER BY [Project1].[No] ASC, [Project1] .[Code] ASC , [Project1]. [C2] ASC


看吧看吧,是不是 EF很聰明的產生一個完全都包含的表格(Table)。


接下來,我們來看第二種 Include 語法的寫法,2種執行結果都相同。

  1. this.richTextBox1.Clear();
  2. this.richTextBox1.AppendText("使用 Eager Loading 機制 Include Method 2 ! \r\n");
  3. this.richTextBox1.AppendText("------------------------------------------------------------------------- \r\n");

  4. using (CRMEF ef = new CRMEF())
  5. {
  6.      var customers = ef.Customer.Include(t => t.CustomerCate).Include(t => t.CustomerEvent);

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

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

  15.           this.richTextBox1.AppendText("\r\r\n");
  16.      }
  17. }

以上介紹完 Eager Loading Inclue 的技巧,希望各位操控 EntityFramework 6 可以更加熟析在最適當的狀況下,來調整預設存取行為機制。



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年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)。




網智數位-軟體開發