Loading AI tools
来自维基百科,自由的百科全书
Entity Framework (又称ADO.NET Entity Framework) 是微软以 ADO.NET 为基础所发展出来的对象关系对应 (O/R Mapping) 解决方案,早期被称为 ObjectSpace,包含在 .NET Framework中发表。从版本6开始独立发布。
原作者 | Microsoft |
---|---|
开发者 | .NET Foundation |
首次发布 | 2008年8月11日 |
当前版本 | |
源代码库 | github github |
编程语言 | C# |
平台 | .NET Framework, .NET Core |
类型 | 对象关系映射(ORM) |
许可协议 | Apache License 2.0 |
网站 | docs |
长久以来,程序员和数据库总是保持着一种微妙的关系,在商用应用程序中,数据库一定是不可或缺的组件,这让程序员一定要为了连接与访问数据库而去学习 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之上支持面向数据应用程序开发,使得开发者工作于领域相关的对象和属性的数据级,如客户、客户地址等,不必关注存储数据的底层表、列。这种在更高层次上创建、维护面向数据的应用程序可以大大节约编码量。[3]
Entity Framework 利用了抽象化数据结构的方式,将每个数据库对象都转换成应用程序对象 (entity),而资料字段都转换为属性 (property),关系则转换为结合属性 (association),让数据库的 E/R 模型完全的转成对象模型,如此让程序员能用最熟悉的编程语言来调用访问。而在抽象化的结构之下,则是高度集成与对应结构的概念层、对应层和存储层,以及支持 Entity Framework 的资料提供者 (provider),让资料访问的工作得以顺利与完整的进行。
2008年8月11日,Entity Framework的第一版(EFv1)随.NET Framework 3.5 Service Pack 1和Visual Studio 2008 Service Pack 1发布。该版受到了广泛的批评。[4]
2010年4月12日,Entity Framework第2版,称为Entity Framework 4.0 (EFv4)发布,解决了第一版的很多问题。[5]该版本完全包含在.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。[6]
2012年8月11日发布了Version 5.0.0。[7]与.NET framework 4.5配套。
2013年10月17日发布了Version 6.0。[8]并成为开源软件,使用Apache License v2. 类似于ASP.NET MVC,开源发布于GitHub。[9] This version has a number of improvements for code-first support.[10]Entity Framework 6.0、6.1、6.2、6.3 和 6.4 完全作为NuGet包提供。
2014年5月19日,微软决定为了让其.NET能跨平台,下一版Entity Framework将完全重写。[11]2016年6月27日,发布了Entity Framework Core 1.0, 伴随着ASP.NET Core 1.0 和 .NET Core 1.0.[12]本来其命名为Entity Framework 7,但为了突出其是完全重写而不是替换EF6所以重命名。[13]
ADO.NET Entity Framework架构,从底向上包括:
概念层结构定义了对象模型 (Object Model),让上层的应用程序码可以如面向对象的方式般访问资料,概念层结构是由 CSDL (Conceptual Schema Definition Language) 所撰写[14]。
一份概念层结构定义如下所示:
<?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) 所撰写[15]。
一份对应层结构定义如下所示:
<?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) 所撰写[16]。
一份存储层结构定义如下所示:
<?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 (EDM) 给出数据的概念模型(CSDL) ,这所使用的建模技术被称为Entity Data Model, 是实体-关系模型的扩展版。[17]数据模型主要描述了实体和其涉及的关联(Association)。EDM模式用Schema Definition Language (SDL)表述,这事XML的一种应用。此外,从概念模式(CSDL)到存储模式(SSDL)的映射(MSL)也必须用XML表示。[18]
Visual Studio提供了Entity Designer 可视化创建EDM和映射规范。该工具输出XML文件(*.edmx)来描述这种模式和映射。Edmx文件包含EF元数据(CSDL/MSL/SSDL内容)。也可手工编辑这3个文件(csdl, msl, ssdl)。
Visual Studio的Entity Data Model Wizard[19]在大部分情况下最初产生数据库模式和概念模式的1对1映射。关系模式下,表和主键、外键组成元素。“实体类型”定义了概念模式下的数据。
实体类型是多个类型字段的聚合,每个字段映射到数据库的特定列,可以包含来自多个物理表的信息。实体类型可以彼此有关系,且独立于物理模式内的关系。相关的实体可以通过字段的名字表示这种关系,以代替从数据库的列获取值,通过这种字段可以遍历相关的实体,返回一个实体或实体集合。
实体类型形成了对象的类,实体是整个类型的实例。实体是应用程序的单个对象,用一个键索引。
ADO.NET Entity Framework使用Entity Data Model (EDM)表示这种映射。
ADO.NET Entity Framework使用eSQL,SQL的一个派生,来执行查询、集合论操作、修改实体及其关系。[20]
实体是“实体类型”的实例,表示单个对象的实例(如“客户”、“订单”)。ADO.NET Entity Framework中的实体属性是完全类型化的,完全兼容于DBMS的类型系统和.NET Framework的Common Type System。属性可以是SimpleType或ComplexType或多值的。所有EntityType属于某个命名空间,并包含一个EntityKey属性。
所有实体实例在EntityContainers中。每个项目有一个或多个EntityContainers。
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。例如,删除一个实体,其关联的关系可以采取:
ADO.NET Entity Framework使用基于XML的数据定义语言称为 Schema Definition Language (SDL)定义EDM Schema。SDL定义了SimpleTypes类似于CTS基础类型,包括String, Int32, Double, Decimal, Guid, 和 DateTime等等。枚举类型定义了基础值和其名字的映射,也被认为是简单类型。ComplexTypes为其他类型的聚合。属性的集合定义了实体类型。写为巴恩斯瑙尔范式:
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用于描述属性的元数据,如是否为可空、缺省值、为单值或多值。
<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对多关系。
<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) 和对象查询产生器方法来撰写。[23]
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,但不限于一个表的结果。
// 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 Framework兼容的data providers都支持的函数,可用于Entity SQL查询。LINQ to Entities的大部门扩展方法都翻译为经典函数。ADO.NET data provider会把经典函数翻译为期望的SQL语句。
类别 | 经典函数[24] |
---|---|
聚类函数 | Avg, BigCount, Count, Max, Min, StDev, StDevP, Sum, Var, VarP |
数学函数 | Abs, Ceiling, Floor, Power, Round, Truncate |
字符串函数 | Concat, Contains, EndsWith, IndexOf, Left, Length, LTrim, Replace, Reverse, Right, RTrim, Substring, StartsWith, ToLower, ToUpper, Trim |
日期时间函数 | AddMicroseconds, AddMilliseconds, AddSeconds, AddMinutes, AddHours, AddNanoseconds, AddDays, AddYears, CreateDateTime, AddMonths, CreateDateTimeOffset, CreateTime, CurrentDateTime, CurrentDateTimeOffset, CurrentUtcDateTime, Day, DayOfYear, DiffNanoseconds, DiffMilliseconds, DiffMicroseconds, DiffSeconds, DiffMinutes, DiffHours, DiffDays, DiffMonths, DiffYears, GetTotalOffsetMinutes, Hour, Millisecond, Minute, Month, Second, TruncateTime, Year |
Bitwise 函数 | BitWiseAnd, BitWiseNot, BitWiseOr, BitWiseXor |
其他函数 | NewGuid |
实现 IEnumerable<T> 泛型接口或 IQueryable<T> 泛型接口的资料来源可以透过 LINQ 进行查询。 实现泛型 IQueryable<T> 接口之泛型 ObjectQuery<T> 类别的执行个体会当做 LINQ to Entities 查询的资料来源。 ObjectQuery<T> 泛型类别表示传回零个或多个具类型对象之集合的查询。 使用 C# 的 var 关键字 (在 Visual Basic 中为 Dim),您也可以让编译器推断实体类型。[25]
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,与查询产生器方法不同。[26]
// 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[27]:
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.