Entity Framework

Entity Framework (又稱ADO.NET Entity Framework) 是微軟以 ADO.NET 為基礎所發展出來的物件關聯對應 (O/R Mapping) 解決方案，早期被稱為 ObjectSpace，包含在 .NET Framework中發表。從版本6開始獨立發布。

## 背景

長久以來，程式設計師和資料庫總是保持著一種微妙的關係，在商用應用程式中，資料庫一定是不可或缺的元件，這讓程式設計師一定要為了連接與存取資料庫而去學習 SQL 指令，因此在資訊業中有很多人都在研究如何將程式設計模型和資料庫整合在一起，物件關聯對應 (Object-Relational Mapping) 的技術就是由此而生，像Hibernate或NHibernate都是這個技術下的產物，而微軟雖然有了ADO.NET這個資料存取的利器，但卻沒有像NHibernate這樣的物件對應工具，因此微軟在.NET Framework 2.0發展時期，就提出了一個ObjectSpace的概念，ObjectSpace可以讓應用程式可以用完全物件化的方法連接與存取資料庫，其技術概念與NHibernate相當類似，然而ObjectSpace工程相當大，在.NET Framework 2.0完成時仍無法全部完成，因此微軟將ObjectSpace納入下一版本的.NET Framework中，並且再加上一個設計的工具（Designer），構成了現在的 ADO.NET Entity Framework。

Entity Framework是ADO.NET之上支持面向數據應用程式開發，使得開發者工作於領域相關的對象和屬性的數據級，如客戶、客戶地址等，不必關注存儲數據的底層表、列。這種在更高層次上創建、維護面向數據的應用程式可以大大節約編碼量。

Entity Framework 利用了抽象化資料結構的方式，將每個資料庫物件都轉換成應用程式物件 (entity)，而資料欄位都轉換為屬性 (property)，關聯則轉換為結合屬性 (association)，讓資料庫的 E/R 模型完全的轉成物件模型，如此讓程式設計師能用最熟悉的程式語言來呼叫存取。而在抽象化的結構之下，則是高度整合與對應結構的概念層、對應層和儲存層，以及支援 Entity Framework 的資料提供者 (provider)，讓資料存取的工作得以順利與完整的進行。

- 概念層：負責向上的物件與屬性顯露與存取。
- 對應層：將上方的概念層和底下的儲存層的資料結構對應在一起。
- 儲存層：依不同資料庫與資料結構，而顯露出實體的資料結構體，和 Provider 一起，負責實際對資料庫的存取和 SQL 的產生。

## 歷史

2008年8月11日，Entity Framework的第一版(EFv1)隨.NET Framework 3.5 Service Pack 1和Visual Studio 2008 Service Pack 1發布。該版受到了廣泛的批評。

2010年4月12日，Entity Framework第2版，稱為Entity Framework 4.0 (EFv4)發布，解決了第一版的很多問題。該版本完全包含在.NET Framework中。

2011年4月12日發布了Entity Framework第3個版本，稱為version 4.1，開始支持Code First。2011年7月25日發布了Entity Framework 4.1 Update 1。

2012年2月29日發布了version 4.3.1。

2012年8月11日發布了Version 5.0.0。與.NET framework 4.5配套。

2013年10月17日發布了Version 6.0。並成為開源軟體，使用Apache License v2. 類似於ASP.NET MVC，開源發布於GitHub。This version has a number of improvements for code-first support.Entity Framework 6.0、6.1、6.2、6.3 和 6.4 完全作為NuGet包提供。

2014年5月19日，微軟決定為了讓其.NET能跨平台，下一版Entity Framework將完全重寫。2016年6月27日，發布了Entity Framework Core 1.0, 伴隨著ASP.NET Core 1.0 和 .NET Core 1.0.本來其命名為Entity Framework 7，但為了突出其是完全重寫而不是替換EF6所以重新命名。

## 架構

ADO.NET Entity Framework架構，從底向上包括：

- Data source specific providers, ADO.NET抽象接口連接到資料庫。
- Map provider, 特定資料庫的provider，翻譯Entity SQL命令樹到資料庫本地SQL方言查詢。包括Store-specific bridge，負責把一般命令樹翻譯為存儲特定的命令樹。
- EDM parser and view mapping, 把數據模型的特定的SDL規範和如何映射到關係模型。從關係模式角度，它創建了對應於概念模型的數據views。它聚合（aggregate）多張表的資訊成為一個實體（entity），把一個到實體的修改（update）分割為到多個表的修改。
- Query and update pipeline, 處理查詢、過濾器、修改等請求，轉化為經典命令樹。
- Metadata services, 處理實體、關係、映射的所有元數據。
- Transactions, 集成基礎存儲的事務能力。如果基礎存儲不支持事務，則在這個層次上實現事務。
- Conceptual layer API, 提供了概念模式下的編程接口。遵從ADO.NET模式，使用Connection對象引用map provider, 使用Command對象發送查詢，返回EntityResultSets 或 EntitySets 以包含結果。
- Disconnected components, ADO.NET Entity Framework使用的本地緩存數據集和實體集，在偶爾連接環境。
- Embedded database: ADO.NET Entity Framework包含一個輕量嵌入資料庫用於客戶端緩存和查詢關係資料庫。
- Design tools, 如Mapping Designer, ADO.NET Entity Framework包含的用於簡化映射從概念模式到關係模式，指出實體類型的哪些屬性映射到資料庫的哪個表。
- Programming layer, 暴露EDM作為編程結構，可被程式語言使用
- Object services, 自動產生CLR類代碼，把同一屬性作為一個實體，允許實體實例作為.NET 對象.
- Web services, 暴露web服務的實體
- High-level services, 如工作在實體上的報告服務。

### 概念層結構

概念層結構定義了物件模型 (Object Model)，讓上層的應用程式碼可以如物件導向的方式般存取資料，概念層結構是由 CSDL (Conceptual Schema Definition Language) 所撰寫。

一份概念層結構定義如下所示：

```xml
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="Employees" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
  <EntityContainer Name="EmployeesContext">
    <EntitySet Name="Employees" EntityType="Employees.Employees" />
  </EntityContainer>
  <EntityType Name="Employees">
    <Key>
      <PropertyRef Name="EmployeeId" />
    </Key>
    <Property Name="EmployeeId" Type="Guid" Nullable="false" />
    <Property Name="LastName" Type="String" Nullable="false" />
    <Property Name="FirstName" Type="String" Nullable="false" />
    <Property Name="Email" Type="String" Nullable="false" />
  </EntityType>
</Schema>
```

### 對應層結構

對應層結構負責將上層的概念層結構以及下層的儲存體結構中的成員結合在一起，以確認資料的來源與流向。對應層結構是由 MSL (Mapping Specification Language) 所撰寫。

一份對應層結構定義如下所示：

```xml
<?xml version="1.0" encoding="utf-8"?>
<Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">

  <EntityContainerMapping StorageEntityContainer="dbo" CdmEntityContainer="EmployeesContext">
    <EntitySetMapping Name="Employees" StoreEntitySet="Employees" TypeName="Employees.Employees">

      <ScalarProperty Name="EmployeeId" ColumnName="EmployeeId" />
      <ScalarProperty Name="LastName" ColumnName="LastName" />
      <ScalarProperty Name="FirstName" ColumnName="FirstName" />
      <ScalarProperty Name="Email" ColumnName="Email" />

    </EntitySetMapping>
  </EntityContainerMapping>
</Mapping>
```

### 儲存層結構

儲存層結構是負責與資料庫管理系統（DBMS）中的資料表做實體對應 (Physical Mapping)，讓資料可以輸入正確的資料來源中，或者由正確的資料來源取出。它是由 SSDL (Storage Schema Definition Language) 所撰寫。

一份儲存層結構定義如下所示：

```xml
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="Employees.Store" Alias="Self"
    Provider="System.Data.SqlClient"
    ProviderManifestToken="2005"
    xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
  <EntityContainer Name="dbo">
    <EntitySet Name="Employees" EntityType="Employees.Store.Employees" />
  </EntityContainer>
  <EntityType Name="Employees">
    <Key>
      <PropertyRef Name="EmployeeId" />
    </Key>
    <Property Name="EmployeeId" Type="uniqueidentifier" Nullable="false" />
    <Property Name="LastName" Type="nvarchar" Nullable="false" MaxLength="50" />
    <Property Name="FirstName" Type="nvarchar" Nullable="false" />
    <Property Name="Email" Type="nvarchar" Nullable="false" />
  </EntityType>
</Schema>
```

## Entity Data Model

Entity Data Model (EDM) 給出數據的概念模型(CSDL)，這所使用的建模技術被稱為Entity Data Model, 是實體-關係模型的擴展版。數據模型主要描述了實體和其涉及的關聯（Association）。EDM模式用Schema Definition Language (SDL)表述，這事XML的一種應用。此外，從概念模式(CSDL)到存儲模式(SSDL)的映射(MSL)也必須用XML表示。

Visual Studio提供了Entity Designer 可視化創建EDM和映射規範。該工具輸出XML文件(*.edmx)來描述這種模式和映射。Edmx文件包含EF元數據(CSDL/MSL/SSDL內容)。也可手工編輯這3個文件(csdl, msl, ssdl)。

### Mapping

Visual Studio的Entity Data Model Wizard在大部分情況下最初產生資料庫模式和概念模式的1對1映射。關係模式下，表和主鍵、外鍵組成元素。「實體類型」定義了概念模式下的數據。

實體類型是多個類型欄位的聚合，每個欄位映射到資料庫的特定列，可以包含來自多個物理表的資訊。實體類型可以彼此有關係，且獨立於物理模式內的關係。相關的實體可以通過欄位的名字表示這種關係，以代替從資料庫的列獲取值，通過這種欄位可以遍歷相關的實體，返回一個實體或實體集合。

實體類型形成了對象的類，實體是整個類型的實例。實體是應用程式的單個對象，用一個鍵索引。

ADO.NET Entity Framework使用Entity Data Model (EDM)表示這種映射。

ADO.NET Entity Framework使用eSQL，SQL的一個派生，來執行查詢、集合論操作、修改實體及其關係。

### 實體

實體是「實體類型」的實例，表示單個對象的實例（如「客戶」、「訂單」）。ADO.NET Entity Framework中的實體屬性是完全類型化的，完全兼容於DBMS的類型系統和.NET Framework的Common Type System。屬性可以是SimpleType或ComplexType或多值的。所有EntityType屬於某個命名空間，並包含一個EntityKey屬性。

所有實體實例在EntityContainers中。每個項目有一個或多個EntityContainers。

EDM基礎類型(簡單類型):

EDM 類型｜CLR 類型映射

Edm.Binary｜Byte

Edm.Boolean｜Boolean

Edm.Byte｜Byte

Edm.DateTime｜DateTime

Edm.DateTimeOffset｜DateTimeOffset

Edm.Decimal｜Decimal

Edm.Double｜Double

Edm.Guid｜Guid

Edm.Int16｜Int16

Edm.Int32｜Int32

Edm.Int64｜Int64

Edm.SByte｜SByte

Edm.Single｜Single

Edm.String｜String

Edm.Time｜TimeSpan

### 關係

任何兩個實體類型可以是相關的，或者是Association關係或者Containment關係。用Relationship Type來表示。

關係類型用degree (arity)和multiplicity刻畫。ADO.NET Entity Framework支持二元雙向關係，multiplicity包括一對一、一對多、多對多。實體間的關係是有名的，稱為Role。定義了關係的目的。

關係類型可以有Operation或Action。例如，刪除一個實體，其關聯的關係可以採取：

- Cascade：刪除關係實例和所有相關聯的實體實例。
- None.

### 模式定義語言

ADO.NET Entity Framework使用基於XML的數據定義語言稱為 Schema Definition Language (SDL)定義EDM Schema。SDL定義了SimpleTypes類似於CTS基礎類型，包括String, Int32, Double, Decimal, Guid, 和 DateTime等等。枚舉類型定義了基礎值和其名字的映射，也被認為是簡單類型。ComplexTypes為其他類型的聚合。屬性的集合定義了實體類型。寫為巴恩斯瑙爾範式：

```ebnf
EntityType ::=
  ENTITYTYPE entityTypeName [BASE entityTypeName]
    [ABSTRACT true|false] KEY propertyName [, propertyName]*
    {(propertyName PropertyType [PropertyFacet]*) +}

PropertyType ::= (
  (PrimitiveType [PrimitiveTypeFacets]*)
    | (complexTypeName)
    | RowType

  PropertyFacet ::= (
    [NULLABLE true | false]
    | [DEFAULT defaultVal]
    | [MULTIPLICITY [1|*]]
  )

  PropertyTypeFacet ::=
    MAXLENGTH | PRECISION | SCALE
    | UNICODE | FIXEDLENGTH | COLLATION
    | DATETIMEKIND | PRESERVESECONDS

  PrimitiveType ::=
    BINARY | STRING | BOOLEAN
    | SINGLE | DOUBLE | DECIMAL | GUID
    | BYTE | SBYTE | INT16 | INT32 | INT64
    | DATETIME | DATETIMEOFFSET | TIME
)
```

Facets用於描述屬性的元數據，如是否為可空、預設值、為單值或多值。

```XML
<ComplexType Name="Addr">
    <Property Name="Street" Type="String" Nullable="false" />
    <Property Name="City" Type="String" Nullable="false" />
    <Property Name="Country" Type="String" Nullable="false" />
    <Property Name="PostalCode" Type="Int32" />
</ComplexType>
<EntityType Name="Customer">
    <Key>
        <PropertyRef Name="Email" />
    </Key>
    <Property Name="Name" Type="String" />
    <Property Name="Email" Type="String" Nullable="false" />
    <Property Name="Address" Type="Addr" />
</EntityType>
```

關係類型，如客戶和訂單是1對多關係。

```XML
<Association Name="CustomerAndOrders">
    <End Type="Customer" Multiplicity="1" />
    <End Type="Orders" Multiplicity="*">
        <OnDelete Action="Cascade" />
    </End>
</Association>
```

## 查詢物件

ADO.NET 實體資料模型工具會產生從 ObjectContext (代表概念模型中所定義的實體容器) 衍生而來的類別。ObjectContext 類別支援針對將實體當成物件傳回之概念模型進行查詢，也支援建立、更新和刪除實體物件。Entity Framework 支援針對概念模型進行物件查詢。這些查詢可以使用 Entity SQL、Language-Integrated Query (LINQ) 和物件查詢產生器方法來撰寫。

### Entity SQL

Entity Client 是 ADO.NET Entity Framework 中的原生使用者端 (Native Client)。它的物件模型和 ADO.NET 的其他使用者端非常相似，一樣有 Connection, Command, DataReader 等物件，但最大的差異就是，它有自己的 SQL 指令 (Entity SQL)，可以用 SQL 的方式存取 EDM。但沒有明確的joins。簡單的說，就是把 EDM 當成一個實體資料庫。

查詢管線分析Entity SQL查詢為一棵命令樹， query into a command tree, 分開多個表上的查詢，再移交EntityClient provider。EntityClient provider使用Connection對象初始化。EntityClient provider再把Entity SQL命令樹轉化為資料庫的本地SQL查詢。查詢返回Entity SQL ResultSet，但不限於一個表的結果。

```csharp
// Initialize the EntityConnectionStringBuilder.
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();

// Set the provider name.
entityBuilder.Provider = providerName;

// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;

// Set the Metadata location.
entityBuilder.Metadata =  @"res://*/AdventureWorksModel.csdl|
                            res://*/AdventureWorksModel.ssdl|
                            res://*/AdventureWorksModel.msl";

Console.WriteLine(entityBuilder.ToString());

using (EntityConnection conn = new EntityConnection(entityBuilder.ToString()))
{
    conn.Open();
    Console.WriteLine("Just testing the connection.");
    conn.Close();
}
```

#### Entity SQL經典函數

Entity Framework兼容的data providers都支持的函數，可用於Entity SQL查詢。LINQ to Entities的大部門擴展方法都翻譯為經典函數。ADO.NET data provider會把經典函數翻譯為期望的SQL語句。

類別｜經典函數

聚類函數｜Avg, BigCount, Count, Max, Min, StDev, StDevP, Sum, Var, VarP

數學函數｜Abs, Ceiling, Floor, Power, Round, Truncate

字串函數日期時間函數

Bitwise 函數｜BitWiseAnd, BitWiseNot, BitWiseOr, BitWiseXor

其他函數｜NewGuid

### LINQ to Entities

實作 IEnumerable 泛型介面或 IQueryable 泛型介面的資料來源可以透過 LINQ 進行查詢。實作泛型 IQueryable 介面之泛型 ObjectQuery 類別的執行個體會當做 LINQ to Entities 查詢的資料來源。ObjectQuery 泛型類別表示傳回零個或多個具型別物件之集合的查詢。使用 C# 的 var 關鍵字 (在 Visual Basic 中為 Dim)，您也可以讓編譯器推斷實體類型。

```csharp
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    ObjectQuery<Product> products = AWEntities.Products;

    // LINQ Query syntax:
    IOrderedQueryable<Product> query =
        from product in products
        orderby product.Name, product.ListPrice descending
        select product;

    // LINQ Method syntax:
    IOrderedQueryable<Product> query = products
        .OrderBy(product => product.Name)
        .ThenByDescending(product => product.ListPrice);
}
```

### 查詢產生器方法

ObjectQuery 類別支援對概念模型進行 LINQ to Entities 和 Entity SQL 查詢。ObjectQuery 也會實作一組查詢產生器方法，這些方法可用來循序建構與 Entity SQL 相等的查詢命令。由於 ObjectQuery 會實作 IQueryable 和 IEnumerable，所以將 ObjectQuery 所實作的查詢產生器方法結合 LINQ 特定的標準查詢運算子方法 (如 First 或 Count) 是可行的。LINQ 運算子並不會傳回 ObjectQuery，與查詢產生器方法不同。

```csharp
// Get the contacts with the specified name.
ObjectQuery<Contact> contactQuery = context.Contact
    .Where("it.LastName = @ln AND it.FirstName = @fn",
    new ObjectParameter("ln", lastName),
    new ObjectParameter("fn", firstName));
```

## 開發工具

目前 ADO.NET Entity Framework 的開發，在 Visual Studio 2008 中有充分的支援，在安裝 Visual Studio 2008 Service Pack 1 後，檔案範本中即會出現 ADO.NET 實體資料模型 (ADO.NET Entity Data Model) 可讓開發人員利用 Entity Model Designer 來設計 EDM，EDM 亦可由Windows記事本等文字編輯器所編輯。

## 衍生服務

微軟特別針對了網路上各種不同的應用程式（例如 AJAX，Silverlight，Mashup 應用程式）開發了一個基於 ADO.NET Entity Framework 之上的服務，稱為 ADO.NET Data Services（專案代號為 Astoria），並與 ADO.NET Entity Framework 一起包裝在 .NET Framework 3.5 Service Pack 1 中發表。

## 支援廠商

目前已有數個資料庫廠商或元件開發商宣布要支援 ADO.NET Entity Framework:

- Mircosoft，支持MsSQL.
- Core Lab，支援Oracle、MySQL、PostgreSQL 與 SQLite 資料庫。
- IBM，實作 DB2 使用的 LINQ Provider。
- MySQL，發展 MySQL Server 所用的 Provider。
- Npqsql，發展 PostgreSQL 所用的 Provider。
- OpenLink Software，發展支援多種資料庫所用的 Provider。
- Phoenix Software International，發展支援 SQLite 資料庫的 Provider。
- Sybase，將支援 Anywhere 資料庫。
- VistaDB Software，將支援 VistaDB 資料庫。
- DataDirect Technologies，發展支援多種資料庫所用的 Provider。
- Firebird，支援 Firebird 資料庫。

## 外部連結

- MSDN Library : ADO.NET Entity Framework
- 讀寫 ADO.NET Entity Framework
- 整合 ADO.NET Entity Framework 到應用程式中
- 首次接觸 ADO.NET Entity Framework
- ADO.NET Entity Framework 概觀
- Recent ADO.NET Entity Framework provider news - Demos and downloads

## 基本資料

- 作者：Microsoft
- 開發者：.NET Foundation
- 發行日期：2008年8月11日
- 原始碼庫：https://github.com/dotnet/ef6、https://github.com/dotnet/efcore
- 程式語言：C#
- 平台：.NET Framework、.NET Core
- 類型：對象關係映射(ORM)
- 許可協議：Apache License 2.0