前言
我要启动一项学习任务,具体计划无,因为后续要学习的知识点当前未知,需要一步步的明确。方法是从基础开始,由浅入深,由点到面,不能光看,还是多练。我将开启我的博客,记录此次的学习历程。
在2011年2月24日,我下了这个决定, 要掌握一些新的技术点,并灵活用之。先是决定参考http://www.cnblogs.com/astar/并对照着http://msdn.microsoft.com/zh-cn/library/bb399572.aspx来学习基本知识。这里,我要感谢陌生的朋友astar。
EDM
实体数据模型
SSDL
基础结构
<edmx:StorageModels>--存储模型
<Schema Namespace="NorthwindModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2000" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">--存储模型定义的根元素。它包括构成存储模型的对象、函数和容器的定义。
<EntityContainer Name="NorthwindModelStoreContainer">--基础数据源的结构
<EntitySet Name="Categories" EntityType="NorthwindModel.Store.Categories" store:Type="Tables" Schema="dbo" />--定义基础数据库中的表或视图
……
<AssociationSet Name="FK_CustomerCustomerDemo" Association="NorthwindModel.Store.FK_CustomerCustomerDemo">
<End Role="CustomerDemographics" EntitySet="CustomerDemographics" />
<End Role="CustomerCustomerDemo" EntitySet="CustomerCustomerDemo" />
</AssociationSet>--表示基础数据库中的两个表之间的外键约束
……
<EntityType Name="Categories">--表名
<Key>
<PropertyRef Name="CategoryID" />
</Key>
<Property Name="CategoryID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="CategoryName" Type="nvarchar" Nullable="false" MaxLength="15" />
<Property Name="Description" Type="ntext" />
<Property Name="Picture" Type="image" />
</EntityType>--描述表结构
<Function Name="CustOrderHist" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo">
<Parameter Name="CustomerID" Type="nchar" Mode="In" />
</Function>--描述存储过程
视图和关系及存储过程,都需要描述。
Schema 元素
下表介绍可应用于 Schema 元素的特性。
特性名称 |
是否必需 |
值 |
Namespace |
是 |
存储模型的命名空间。 Namespace 特性的值用于构成类型的完全限定名称。 例如,如果名为 Customer 的 EntityType 位于 ExampleModel.Store 命名空间中,则 EntityType 的完全限定名称为 ExampleModel.Store.Customer。 不能将下面的字符串用作 Namespace 特性的值:System、Transient 或 Edm。 Namespace 特性的值不能与 CSDL Schema 元素中 Namespace 特性的值相同。 |
Alias |
否 |
用于取代命名空间名称的标识符。 例如,如果名为 Customer 的 EntityType 位于 ExampleModel.Store 命名空间中并且 Alias 特性的值为 StorageModel,则可以将 StorageModel.Customer 用作 EntityType 的完全限定名称。 |
Provider |
是 |
数据提供程序。 有关更多信息,请参见实体框架数据提供程序。 |
ProviderManifestToken |
是 |
一个标记,该标记指示提供程序清单返回到的提供程序。 没有为该标记定义格式。 标记的值由提供程序定义。 有关 SQL Server 提供程序清单标记的信息,请参见用于实体框架的 SQL Server .NET Framework 数据提供程序 (SqlClient)。 |
EntitySet 元素
特性名称 |
是否必需 |
值 |
Name |
是 |
实体集的名称。 |
EntityType |
是 |
实体集包含其实例的实体类型的完全限定名称。 |
Schema |
否 |
数据库架构。 |
Table |
否 |
数据库表。 |
AssociationSet 元素
特性名称 |
是否必需 |
值 |
Name |
是 |
关联集表示的外键约束的名称。 |
Association |
是 |
定义参与外键约束的列的关联的名称。 |
CSDL
实体由 EntityType 元素表示,关系由 Association 元素表示。“NavigationProperty”的一种特殊类型的属性。这定义了如何按照关联从一个实体导航到另一个实体。
基础结构
<edmx:ConceptualModels>--概念模型
<Schema Namespace="NorthwindModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">--概念模型定义的根元素
<EntityContainer Name="NorthwindEntities">--用于容纳实体集、关联集和函数导入的逻辑容器
<EntitySet Name="Categories" EntityType="NorthwindModel.Categories" />--定义对象实体
……
<AssociationSet Name="FK_Products_Categories" Association="NorthwindModel.FK_Products_Categories">
<End Role="Categories" EntitySet="Categories" />
<End Role="Products" EntitySet="Products" />
</AssociationSet>--定义对象关系
……
<EntityType Name="Categories">
<Key>
<PropertyRef Name="CategoryID" />
</Key>
<Property Name="CategoryID" Type="Int32" Nullable="false" />
<Property Name="CategoryName" Type="String" Nullable="false" MaxLength="15" Unicode="true" FixedLength="false" />
<Property Name="Description" Type="String" MaxLength="Max" Unicode="true" FixedLength="false" />
<Property Name="Picture" Type="Binary" MaxLength="Max" FixedLength="false" />
<NavigationProperty Name="Products" Relationship="NorthwindModel.FK_Products_Categories" FromRole="Categories" ToRole="Products" />
</EntityType>--描述对象
……
<Association Name="FK_Products_Categories">
<End Role="Categories" Type="NorthwindModel.Categories" Multiplicity="0..1" />
<End Role="Products" Type="NorthwindModel.Products" Multiplicity="*" />
</Association>--描述对象关系
Property 元素 (CSDL)
EntityType 和 ComplexType 元素应用程序
下表介绍可应用于 Property 元素的特性。
特性名称 |
是否必需 |
值 |
Name |
是 |
属性的名称。 |
Type |
是 |
属性值的类型。 属性值类型必须为模型作用域内的 EDMSimpleType 或复杂类型(由完全限定名称指示)。 有关更多信息,请参见概念模型类型。 |
Nullable |
否 |
True (默认值)或 False,具体取决于属性是否可以具有 null 值。 在由 http://schemas.microsoft.com/ado/2006/04/edm 命名空间指示的 CSDL 版本中,复杂类型属性必须具有 Nullable="False"。 |
DefaultValue |
否 |
属性的默认值。 |
MaxLength |
否 |
属性值的最大长度。 |
FixedLength |
否 |
True 或 False,具体取决于属性值是否将作为固定长度字符串存储。 |
Precision |
否 |
属性值的精度。指定小数点左边和右边可以存储的十进制数字的最大个数。精度必须是从 1 到最大精度之间的值。最大精度为 38。 |
Scale |
否 |
属性值的刻度。(小数位数) 指定小数点右边可以存储的十进制数字的最大个数。小数位数必须是从 0 到 p 之间的值。默认小数位数是 0,因而 0 <= s <= p。最大存储大小基于精度而变化。 |
Unicode |
否 |
True 或 False,具体取决于属性值是否将作为 Unicode 字符串存储。Unicode来保存的字符,会将单个中文、英文字符作为一个字符处理 。 |
Collation |
否 |
指定要在数据源中使用的排序序列的字符串。 |
ConcurrencyMode |
否 |
None (默认值)或 Fixed。 如果值设置为 Fixed,则属性值将用于开放式并发检查。 |
| ||
可以将任何数量的批注特性(自定义 XML 特性)应用于 Property 元素。 然而,自定义特性可能不属于为 CSDL 保留的任何 XML 命名空间。 任何两个自定义特性的完全限定名称都不能相同。 |
示例
下面的示例演示一个具有三个 Property 元素的 EntityType 元素:
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>
下面的示例演示一个具有五个 Property 元素的 ComplexType 元素:
<Property Type="String" Name="StreetAddress" Nullable="false" />
<Property Type="String" Name="City" Nullable="false" />
<Property Type="String" Name="StateOrProvince" Nullable="false" />
<Property Type="String" Name="Country" Nullable="false" />
<Property Type="String" Name="PostalCode" Nullable="false" />
</ComplexType>
MSL
基础结构
<edmx:Mappings>--从 CSDL 到 SSDL 的直接映射
<Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">--为映射规范的根元素
<EntityContainerMapping StorageEntityContainer="NorthwindModelStoreContainer" CdmEntityContainer="NorthwindEntities">--将概念模型中的实体容器映射到存储模型中的实体容器。
<EntitySetMapping Name="Categories">
<EntityTypeMapping TypeName="IsTypeOf(NorthwindModel.Categories)">
<MappingFragment StoreEntitySet="Categories">
<ScalarProperty Name="CategoryID" ColumnName="CategoryID" />
<ScalarProperty Name="CategoryName" ColumnName="CategoryName" />
<ScalarProperty Name="Description" ColumnName="Description" />
<ScalarProperty Name="Picture" ColumnName="Picture" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>--描述对象属性与表字段对应
……
<AssociationSetMapping Name="FK_Products_Categories" TypeName="NorthwindModel.FK_Products_Categories" StoreEntitySet="Products">
<EndProperty Name="Categories">
<ScalarProperty Name="CategoryID" ColumnName="CategoryID" />
</EndProperty>
<EndProperty Name="Products">
<ScalarProperty Name="ProductID" ColumnName="ProductID" />
</EndProperty>
<Condition ColumnName="CategoryID" IsNull="false" /> </AssociationSetMapping>--描述对象关系与表结构关系对应
将模型的 EntitySet 映射到存储的 EntitySet 需要 EntitySetMapping 元素。TypeName 属性定义模型中 EntitySet 的名称,而 StoreEntitySet 属性定义存储中与之相对应的 EntitySet 的名称。通过 ScalarProperty 元素可将模型中的每个属性映射到存储。
访问EDM
可通过三种不同机制中的任意一种实现对实体框架的概念模型的访问。
使用 EntityClient
当 EntityClient 使用自己的名为“Entity SQL”的基于文本的语言与概念模型通信时,会将其从逻辑存储中提取出来。所有使用 EntityClient 执行的Entity SQL 查询均被编译成发送到存储的命令树。查询从概念模型的Entity SQL 到存储的向下转换由实体框架处理。
EntityClient 中的类与常见的 ADO.NET 提供程序中的类相似。例如,使用 EntityCommand 对象执行 EntityClient 查询,这需要 EntityConnection 对象连接到 EDM。当 EntityClient 与 EDM 中的实体交互时,EntityClient 不返回实体的实例而返回 DbDataReader 对象中的所有结果。EntityClient 可以返回一组标准行和列,也可以通过 DbDataReader 返回更复杂的分层数据的表示形式。
示例使用 EntityClient 连接到概念模型并检索来自伦敦的客户列表。EntityConnection 可以接受概念层的完整连接字符串或 App.Config 文件中连接字符串的名称。连接字符串包含元数据文件(CSDL、MSL 和 SSDL 文件)列表,以及存储的专用于数据库的连接字符串信息。
using System.Data;
using System.Data.EntityClient;
using System.Configuration;
...
//string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["NorthwindEntities"].ConnectionString;
//CSharpTestEntities edm = new CSharpTestEntities();
//string connStr = edm.Connection.ConnectionString;
string connStr = "name = NorthwindEntities"; //使用三种方法获取连接字符串
using (EntityConnection conn = new EntityConnection(connStr))
{
string sqlStr = " SELECT VALUE c FROM NorthwindEntities.Customers AS c";
conn.Open();
EntityCommand ecmd = new EntityCommand(sqlStr, conn);
//ExecuteDbDataReader、ExecuteNonQuery 、ExecuteReader 、ExecuteScalar等
EntityDataReader edr = ecmd.ExecuteReader(CommandBehavior.SequentialAccess);
while (edr.Read())
{
Console.WriteLine(edr["CompanyName"]);
}
Console.WriteLine(ecmd.ToTraceString());
}
Console.Read();
使用对象服务
与由 EDM 表示的数据进行交互的另一种方法是使用对象服务。通过对象服务,可以加载对象和导航在 EDM 中定义的任何关系。如图 1 所示,对象服务使用 EntityClient 来获取数据。对象服务增添了身份解析,使用 DataSet 时,该身份解析是手动过程。它还提供了对象持久性和事件的跟踪更改以允许显式加载和保存。这将缩短与服务器的往返路程。
对象服务允许直接返回对象列表(即可同时返回投影和定义的实体)。例如,使用对象服务,您可以按 EDM 中的定义检索 List<Customers>。可以检查 Customers 对象,更改值,然后将数据再次保存到数据库中。
如果将投影与对象服务结合使用,则返回的数据将是不可更新的对象。由于投影返回实体的特定属性而不是整个实体,对象服务无法将投影数据的更新再次保存到数据库中。如果您要更新数据,更好的选择是返回整个实体而不是使用投影。
您可以使用对象服务以使用Entity SQL 执行查询,也可以使用 LINQ to Entities 编写查询。下面的示例演示了如何使用对象服务和Entity SQL 进行查询以检索 Customers 列表:
using System.Data.Objects;
...
using (var edm = new NorthwindEntities())
{
string esql = "select value c from NorthwindEntities.Customers as c order by c.CustomerID limit 10";
ObjectQuery<Customers> query = edm.CreateQuery<Customers>(esql);
ObjectResult<Customers> results = query.Execute(MergeOption.NoTracking);
foreach (Customers c in results)
Console.WriteLine(c.CustomerID + ":" + c.CompanyName);
Console.WriteLine(query.ToTraceString());
}
在 EDM 中,EntityContainer 由从 ObjectContext(在本示例中为 northwindContext)继承的类表示。ObjectContext 类实施 ObjectQuery<T> 接口,从而使其可以使用Entity SQL 或 LINQ 创建查询。
CreateQuery 方法接受参数化的Entity SQL 语句,该语句定义了将检索 Customers 实体列表的查询。通过使用 foreach 语句对 ObjectQuery<Customers> 进行迭代时将执行作用于数据库的实际 SQL 语句。
使用 LINQ to Entities
可以在Entity SQL 中编写动态查询,并与对象服务一起使用来与 EDM 实体交互。但是,实体框架还可以与使用 LINQ to Entities 强类型化 EDM 类结合使用。例如,在刚才显示的示例中,可以将使用对象服务和Entity SQL 进行查询修改为使用 LINQ to Entities 进行查询,如下所示:
ObjectQuery<Customers> customersList = edm.Customers;
IQueryable<Customers> customersResult = from customers in customersList
where customers.City == "London"
select customers;
foreach (Customers c in customersResult)
Console.WriteLine(c.CustomerID + ":" + c.CompanyName);
此代码示例使用由 C# 3.0 支持的强类型化 LINQ 语法替代了Entity SQL 的所有基于文本的语法。
练习下载