[索引页]
[×××]
精进不休 .NET 4.0 (7) - ADO.NET Entity Framework 4.0 新特性
做者:
webabcd
介绍
ADO.NET Entity Framework 4.0 的新增功能
- 对外键的支持,即把外键当作实体的一个属性来处理
- 对复杂类型的支持,即实体属性能够是一个复杂类型
- 将多个表映射到一个概念实体,将一个表拆为多个概念实体
- 加强了 LINQ to Entities
- 新增了对 POCO(Plain Old CLR Object)的支持,即 Model 代码中不会有任何关于持久化的代码
- 其余新特性
示例
一、外键 的 Demo
EntityFramework/ForeignKeys/Demo.aspx.cs
/*
* ADO.NET Entity Framework 4.0 - 新增了对外键的支持,即把外键当作实体的一个属性来处理
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DataAccess.EntityFramework.ForeignKeys
{
public partial
class Demo : System.Web.UI.Page
{
Random _random =
new Random() Random _random =
new Random();
void Page_Load() void Page_Load(object sender, EventArgs e)
{
// 在一个已存在的产品类别下新建一个产品(经过外键值)
using (var ctx =
new ForeignKeysEntities())
{
Product p =
new Product
{
Name =
"webabcd test" + _random.
Next().ToString(),
ProductNumber = _random.
Next().ToString(),
StandardCost = 1,
ListPrice = 1,
SellStartDate = DateTime.Now,
rowguid = Guid.NewGuid(),
ModifiedDate = DateTime.Now,
ProductCategoryID = 18
};
// 这里须要手工 Add 这个新的 Product,而后再调用 SaveChanges()
ctx.Products.AddObject(p);
Response.Write(ctx.SaveChanges());
}
Response.Write(
"<br /><br />");
// 在一个已存在的产品类别下新建一个产品(经过外键对象)
using (var ctx =
new ForeignKeysEntities())
{
Product p =
new Product
{
Name =
"webabcd test" + _random.
Next().ToString(),
ProductNumber = _random.
Next().ToString(),
StandardCost = 1,
ListPrice = 1,
SellStartDate = DateTime.Now,
rowguid = Guid.NewGuid(),
ModifiedDate = DateTime.Now,
ProductCategory = ctx.ProductCategories.Single(c => c.ProductCategoryID == 18)
};
// 这里直接调用 SaveChanges() 便可,而不用再手工地 Add 这个新的 Product
// 由于与这个新的 Product 关联的那个已存在的 ProductCategory 会自动地 Add 这个新的 Product
Response.Write(ctx.SaveChanges());
}
}
}
}
二、复杂类型的 Demo
EntityFramework/ComplexType/Demo.aspx.cs
/*
* ADO.NET Entity Framework 4.0 - 新增了对复杂类型的支持,即实体属性能够是一个复杂类型
* 一、在 EDM 设计器中的实体上,点击右键,在“Add”选项中能够新建一个复杂类型
* 二、在 EDM 设计器中的实体上,选中多个属性后,点击右键,选择“Refactor into
New Complex Type”能够合并多个属性为一个复杂类型
* 三、在 EDM 设计器中的“Mapping Details”窗口或“Model Broswer”窗口里,能够对复杂类型作编辑
*
* ADO.NET Entity Framework 4.0 - 对存储过程的支持有了明显的加强
* 表现为:能够将存储过程的返回值映射到一个自定义的复杂类型上,固然,这个复杂类型也能够根据储过程的返回值自动生成
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DataAccess.EntityFramework.ComplexType
{
public partial
class Demo : System.Web.UI.Page
{
void Page_Load() void Page_Load(object sender, EventArgs e)
{
using (var ctx =
new ComplexTypeEntities())
{
// 这里的 Name 类型是自定义的一个复杂类型(其有三个属性,分别为FirstName, MiddleName, LastName),详见 EDM
Name name = ctx.Customers.First().Name;
Response.Write(
string.Format(
"FirstName: {0}<br />MiddleName: {1}<br />LastName: {2}", name.FirstName, name.MiddleName, name.LastName));
}
Response.Write(
"<br /><br />");
using (var ctx =
new ComplexTypeEntities())
{
// 这里的 MyCustomer 类型,是存储过程 uspSelectCustomer(其概念模型为:GetCustomer()) 的返回值的映射类型
MyCustomer customer = ctx.GetCustomer().First();
Response.Write(
string.Format(
"CustomerID: {0}<br />FirstName: {1}<br />MiddleName: {2}<br />LastName: {3}", customer.CustomerID, customer.FirstName, customer.MiddleName, customer.LastName));
}
}
}
}
三、将一个表拆为多个概念实体的 Demo
EntityFramework/TableSplitting/Demo.aspx.cs
/*
一、将多个表映射到一个概念实体,原来就能够。在 EDM 设计器中将两个一对一的表映射到一个实体便可
二、将一个表拆为多个概念实体,原来也行,可是要在 xml 中手工配置。如今 VS2010 中只需在 EDM 设计器中作以下设置:
a、新建两个实体,作好相关字段相对于原表的映射
b、在这两个实体间新建一个一对一的关联
c、双击这个关联线,编辑约束,指明主表和依赖表,并设置相关的主键
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DataAccess.EntityFramework.TableSplitting
{
public partial
class Demo : System.Web.UI.Page
{
void Page_Load() void Page_Load(object sender, EventArgs e)
{
// 将一个 ErrorLog 表映射到两个实体上 ErrorLog 和 ErrorLogExt,详见 EDM
using (var ctx =
new TableSplittingEntities())
{
ErrorLog log = ctx.ErrorLogs.First();
Response.Write(log.ErrorLogID);
Response.Write(
"<br />");
log.ErrorLogExtReference.Load();
Response.Write(log.ErrorLogExt.ErrorMessage);
}
}
}
}
四、LINQ to Entities 新功能的 Demo
EntityFramework/LINQ2Entities/Demo.aspx.cs
/*
* ADO.NET Entity Framework 4.0 - 加强了 LINQ
to Entities
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.Objects;
using System.Data.Common;
namespace DataAccess.EntityFramework.LINQ2Entities
{
public partial
class Demo : System.Web.UI.Page
{
void Page_Load() void Page_Load(object sender, EventArgs e)
{
Demo1();
Demo2();
// 支持 Single() 扩展方法了,以前的版本不支持
}
void Demo1() void Demo1()
{
// ADO.NET Entity Framework 4.0 - 新增了 System.Data.Objects.EntityFunctions 和 System.Data.Objects.SqlClient.SqlFunctions
// 其做用至关于 Linq
to Sql 中的 System.Data.Linq.SqlClient.SqlMethods
using (var ctx =
new LINQ2EntitiesEntities())
{
var products =
from p
in ctx.Products
where System.Data.Objects.SqlClient.SqlFunctions.DateDiff(
"year", p.SellStartDate, DateTime.Now) <= 10
select p;
Response.Write((products
as System.Data.Objects.ObjectQuery).ToTraceString());
Response.Write(
"<br />");
Response.Write(
"十年内销售的产品数量为:" + products.Count());
}
Response.Write(
"<br /><br />");
// 上面的示例若是写成 esql 就是以下的写法。固然这个原来就支持。
using (var ctx =
new LINQ2EntitiesEntities())
{
string esql =
"select value p from LINQ2EntitiesEntities.Products as p where SqlServer.DATEDIFF('year', p.SellStartDate, SqlServer.GETDATE()) <= 10";
//
string esql =
"using SqlServer; select value p from LINQ2EntitiesEntities.Products as p where DATEDIFF('year', p.SellStartDate, GETDATE()) <= 10";
ObjectQuery<Product> products = ctx.CreateQuery<Product>(esql);
Response.Write(products.ToTraceString());
Response.Write(
"<br />");
Response.Write(
"十年内销售的产品数量为:" + products.Count());
}
Response.Write(
"<br /><br />");
}
void Demo2() void Demo2()
{
// 使用 esql 的方式调用 sql 中的用户自定义函数
using (var ctx =
new LINQ2EntitiesEntities())
{
string esql =
"select value top(1) LINQ2EntitiesModel.Store.ufnGetFullName(c.firstName, c.middleName, c.lastName) from LINQ2EntitiesEntities.Customers as c";
ObjectQuery<
string> customers = ctx.CreateQuery<
string>(esql);
Response.Write((customers
as System.Data.Objects.ObjectQuery).ToTraceString());
Response.Write(
"<br />");
foreach (var customerName
in customers.ToList())
{
Response.Write(customerName);
Response.Write(
"<br />");
}
}
Response.Write(
"<br /><br />");
// clr 的方式调用 sql 的用户自定义函数。具体实现见 MyClass 类
using (var ctx =
new LINQ2EntitiesEntities())
{
var customers =
from c
in ctx.Customers
select MyClass.GetFullName(c.FirstName, c.MiddleName, c.LastName);
customers = customers.Take(1);
Response.Write((customers
as System.Data.Objects.ObjectQuery).ToTraceString());
Response.Write(
"<br />");
foreach (var customerName
in customers.ToList())
{
Response.Write(customerName);
Response.Write(
"<br />");
}
}
}
public static
class MyClass
{
// System.Data.Objects.DataClasses.EdmFunction(
string namespaceName,
string functionName) - 将 sql 中的指定的用户自定义函数映射到 clr 的方法上
//
string namespaceName - SSDL(存储模型)的命名空间,能够在 edmx 文件中找到这个值
//
string functionName - sql 中的用户自定义函数名
[System.Data.Objects.DataClasses.EdmFunction(
"LINQ2EntitiesModel.Store",
"ufnGetFullName")]
/// <summary>
/// 此方法的参数要与其所映射的 sql 用户自定义函数的参数相匹配
/// 此方法只可用于 linq 表达式,方法内不用作任何实现
/// </summary>
static
string GetFullName() static
string GetFullName(
string firstName,
string middleName,
string lastName)
{
throw
new NotSupportedException(
"You can only call this method as part of a LINQ expression");
}
}
}
}
五、POCO 的 Demo
Demo.aspx
<%@ Page Language=
"C#" AutoEventWireup=
"true" CodeBehind=
"Demo.aspx.cs"
Inherits=
"POCODemo.Demo" %>
<!DOCTYPE html
PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns=
"http://www.w3.org/1999/xhtml">
<head runat=
"server">
<title></title>
</head>
<body>
<form id=
"form1" runat=
"server">
<div>
ADO.NET Entity Framework 4.0 - 新增了对 POCO(Plain Old CLR Object)的支持,即 Model 代码中不会有任何关于持久化的代码
<ul>
<li>一、线上有 POCO 的 T4(Text Template Transformation Toolkit)模板</li>
<li>二、在 EDM 设计器上单击右键,选择“Add Code Generation Item”,在线上模板中选择“ADO.NET C# POCO Entity Generator”模板生成便可</li>
<li>三、在 EF 中,POCO 与非 POCO 不能在一个项目中共存,由于非 POCO 的 EF 在 assembly 级别上会有以下声明<br />
using System.Data.Objects.DataClasses;<br />
[assembly: EdmSchemaAttribute()]<br />
而 POCO 不须要这个声明,因此一个程序集内不能既有 POCO 又有非 POCO </li>
<li>四、具体的 POCO 代码,详见本例中的由 POCO 模板生成的代码 </li>
</ul>
</div>
</form>
</body>
</html>
六、其余新特性
EntityFramework/Others.aspx
<%@ Page Title=
"其它,一笔带过" Language=
"C#" MasterPageFile=
"~/Site.Master" AutoEventWireup=
"true"
CodeBehind=
"Others.aspx.cs"
Inherits=
"DataAccess.EntityFramework.Others" %>
<asp:Content ID=
"Content1" ContentPlaceHolderID=
"head" runat=
"server">
</asp:Content>
<asp:Content ID=
"Content2" ContentPlaceHolderID=
"ContentPlaceHolder1" runat=
"server">
<p>
一、T4 模板引擎(微软的一个代码生成引擎) - Text Template Transformation Toolkit
</p>
<p>
二、加强了 EDM 设计器
</p>
<p>
三、对 Model-First 的支持,即根据概念模型生成存储模型和映射模型
<ul>
<li>在概念模型(EDM 设计器)上单击右键,选择“ Generate Database from Model”,便可生成数据库脚本</li>
<li>
在 EDM 设计器中,与 Model-First 相关的字段属性说明
<ul>
<li>StoreGeneratedPattern - 该字段所对应的数据库中的列属性(有三种:无,自增,经过计算而来)</li>
<li>FixedLength - FixedLength=
true 对应 nchar, FixedLength=
false 对应 nvarchar</li>
<li>Unicode - 是不是 Unicode 编码。好比字符串若是是非 Unicode 则对应 varchar,若是是 Unicode 则对应 nvarchar</li>
<li>Max Length - 最大字符数。对应 varchar(n) 或 nvarchar(n) 中的 n</li>
</ul>
</li>
</ul>
</p>
<p>
四、Code Only - 在 POCO 的基础上,连 EDM 也不须要了(即不用再作概念模型,映射模型,存储模型的配置), 纯写代码便可,惋惜在 EF 4.0 的正式版里这个功能被去掉了
</p>
<p>
五、 改进了 SQL 语句的生成
</p>
<p>
六、Lazy Loading - 支持延迟加载,相关设置 context.ContextOptions.DeferredLoadingEnabled =
true; 其默认值就是
true
</p>
<p>
七、
Explicit Loading - 显示加载,看下面的例子
<ul>
<li>加载导航属性的方法以下(固然 Include 也能够达到一样的效果)context.LoadProperty(category,
"Products");</li>
<li>上面那个方法(包括 Include)不太好,由于若是实体集名称写错的话 runtime 的时候是才能发现,因此为了不写错可使用以下方法 context.LoadProperty(category, c => c.Products);</li>
</ul>
</p>
<p>
八、几种自带的 T4 模板的说明
<ul>
<li>ADO.NET EntityObject Generator - 把 edmx 文件中的内联代码摘出来</li>
<li>ADO.NET POCO Entity Generator - 生成 POCO(Plain Old CLR Object) 实体,其包括每一个表所映射的实体及一个Context,POCO 中不会包含持久化相关的代码(这个模板非内置,能够在线上模板中找到)</li>
<li>ADO.NET Self-Tracking Entity Generator - POCO 的增强版,在 POCO 的基础上增长了实体状态自跟踪的功能</li>
</ul>
</p>
<p>
九、新建 EDM 的时候,在向导中有一个选项“pluralize
or singularize generated object names”,其意思为:生成对应的 Model 时,实体名称自动用单数形式,实体集名称自动用复数形式
</p>
</asp:Content>