Entity Framework 6 Recipes 2nd Edition(11-1)译 -> 从“模型定义”函数返回一个标量值
第11章函数
函数提供了一个有力代码复用机制, 并且让你的代码保持简洁和易懂。
它们同样也是EF运行时能利用的数据库层代码.函数有几类: Rowset Functions, 聚合函数, Ranking Functions, 和标量值函数.
函数要么确定,要么不确定。当用一些指定的值调用函数,而函数返回的结果总是一样时,它就是确定的函数。当甚至用同样的一些值调用时,而函数每次返回的结果也可能不一样,它就是不确定的函数。
在前七小节,我们探讨“模型定义”的函数,这些函数允许我们在概念层上创建。这些函数依照EF类型和你的模型实体来定义。这样使得它们能便捷地通过数据存储执行。
在剩下的小节,我们就展示如何使用被EF定义的函数和数据库层的函数.
这些函数允许你影响已有的代码,在EF运行时或更接近于你数据的数据库层.
11-1. 从“模型定义”函数返回一个标量值
问题
想在概念模型定义一个函数,接受一个实体的实例,并且返回一个标量值.
解决方案
假设已有一个如Figure 11-1.所示模型
Figure 11-1. 一个产品和分类的模型
接下来创建一个接受一个Category 实体实例并返回给定Category 里所有product的平均单价:
1. 在解决方案资源管理器中,右击 .edmx 文件,选择“打开方式” ➤ XML 编辑器.
2.在.edmx文件的概念模型(conceptual models)节点<Schema>标签里,插入Listing 11-1里的代码,这样就在模型里定义了函数。
Listing 11-1. Definition of the AverageUnitPrice() Function in the Model
<Function Name="AverageUnitPrice" ReturnType="Edm.Decimal">
<Parameter Name="category" Type="EFRecipesModel1101.Category" />
<DefiningExpression>
ANYELEMENT(Select VALUE Avg(p.UnitPrice) from EFRecipesEntities1101.Products as p where p.Category == category)
</DefiningExpression>
</Function>
3. 插入和查询这个模型的代码,如Listing 11-2所示.
class Program
{
static void Main(string[] args)
{
RunExample();
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
static void RunExample()
{
using (var context = new EFRecipesEntities1101())
{
context.Database.ExecuteSqlCommand("delete from chapter11.product;delete from chapter11.category");
var c1 = new Category { CategoryName = "Backpacking Tents" };
var p1 = new Product
{
ProductName = "Hooligan",
UnitPrice = 89.99M,
Category = c1
};
var p2 = new Product
{
ProductName = "Kraz",
UnitPrice = 99.99M,
Category = c1
};
var p3 = new Product
{
ProductName = "Sundome",
UnitPrice = 49.99M,
Category = c1
};
context.Categories.Add(c1);
context.Products.Add(p1);
context.Products.Add(p2);
context.Products.Add(p3);
var c2 = new Category { CategoryName = "Family Tents" };
var p4 = new Product
{
ProductName = "Evanston",
UnitPrice = 169.99M,
Category = c2
};
var p5 = new Product
{
ProductName = "Montana",
UnitPrice = 149.99M,
Category = c2
};
context.Categories.Add(c2);
context.Products.Add(p4);
context.Products.Add(p5);
context.SaveChanges();
}
// with eSQL
using (var context = new EFRecipesEntities1101())
{
Console.WriteLine("Using eSQL for the query...");
Console.WriteLine();
string sql = @"Select c.CategoryName, EFRecipesModel1101
.AverageUnitPrice(c) as AveragePrice from
EFRecipesEntities1101.Categories as c";
var objectContext = (context as IObjectContextAdapter).ObjectContext;
var cats = objectContext.CreateQuery<DbDataRecord>(sql);
foreach (var cat in cats)
{
Console.WriteLine("Category '{0}' has an average price of {1}",
cat["CategoryName"], ((decimal)cat["AveragePrice"]).ToString("C"));
}
}
// with LINQ
using (var context = new EFRecipesEntities1101())
{
Console.WriteLine();
Console.WriteLine("Using LINQ for the query...");
Console.WriteLine();
var cats = from c in context.Categories
select new
{
Name = c.CategoryName,
AveragePrice = MyFunctions.AverageUnitPrice(c)
};
foreach (var cat in cats)
{
Console.WriteLine("Category '{0}' has an average price of {1}",
cat.Name, cat.AveragePrice.ToString("C"));
}
}
}
}
public class MyFunctions
{
[EdmFunction("EFRecipesModel1101", "AverageUnitPrice")]
public static decimal AverageUnitPrice(Category category)
{
throw new NotSupportedException("Direct calls are not supported!");
}
}
Listing 11-2.用 “模型定义” 的AverageUnitPrice()函数插入和查询模型
输出结果如下面的 Listing 11-2所示:
Using eSQL for the query...
Category 'Backpacking Tents' has an average price of $79.99
Category 'Family Tents' has an average price of $159.99
Using LINQ for the query...
Category 'Backpacking Tents' has an average price of $79.99
Category 'Family Tents' has an average price of $159.99
它是如何工作的?
“模型定义”的函数,在概念层创建,并且用eSQL来写. 当然, “模型定义”允许你引用你模型中的实体,就像我们这里做的这样,在函数的实现中引用了Category 实体和Product 实体以及它们之间的关系。函数带来的额外好处是:我们不会被绑定在一个指定的存储层上。把函数放在更低的层,甚至是数据库驱动, 我们的程序也可以工作.
目前的设计器不支持“模型定义”函数,不像存储过程,能被设计器支持,“模型定义”函数不会被模型浏览器显示也不会出现在设计器的其它地方。设计器也不会检查eSQL中的语法错误,只有在运行时才会报错,但至少可打开.edmx来定义。
在Listing 11-2,代码先插入两个类别(category)和各自的一些产品(product).之后用两种略微不同的方式查询这些数据。在第一个查询例子,我们创建eSQL语句来调用AverageUnitPrice() 函数.并执行查询. 在查询结果中的每行,我们取出第一列数据(category名称)和第二列数据(每个类别产品的平均单价). 并且输出。
第二个查询例子,更有趣,我们在LINQ查询中使用AverageUnitPrice()函数,不过需要先在另一个类里添加一个方法存根,方法用 [EdmFunction()] 特性装饰, 把它标记为是一个“模型定义”函数. 运行时方法不可以调用它(一旦调用,方法中就显式抛出异常). 因为我们只是返回一个标量值,所以这个方法签名比较简单(参数个数,类型,和返回值类型). 在In the LINQ 查询中query, 我们获取每个category并且把结果(category名称,调用MyFunction类里AverageUnitPrice()方法返回的结果)映射到一个匿名类. 并且输出。
DbContext是 ObjectContext轻量级的版本. 每当需要执行eSql (Entity SQL)时, 是必须使用ObjectContext 的. 因为我们要通过DbContext获取ObjectContext (使用:(context as IObjectContextAdapter) ObjectContext).
“模型定义”函数的参数可以是:标量值,实体类,复杂类型,匿名类型,或是上述类型的集合).在本章的很多小节,我们就演示如何创建和使用这些类型参数的“模型定义”函数。
“模型定义” 函数的参数没有方向性,没有“输出”参数,只有“输入”参数,原因是“模型定义” 函数只是一个”组件”,并且能成为LINQ查询的一部分。
在这个例子中,我们返回单一的标量decimal类型的值。因为Select查询结果会被理解成一个集合,所以我们需要为返回的结果显式地使用AnyElement运算符。EF不知道如何把一个集合映射成一个标量值,所以我们在这儿使用AnyElement运算符告诉它返回的结果只是一个元素。当Select结果只有一个元素的时候,我们也会运用该运算符告诉调用者它只是一个元素。
最佳实践
“模型定义”函数提供了一个纯净和有效的概念模型的组成部分.下面列几个它的最佳实践:.
>“模型定义”函数用eSQL 定义到概念层. 这使用我们可以从存储模型细节中抽象出一个更完成的模型.
>你可以把LINQ或eSQL查询中常用的表达定义成函数. 这样使代码组织结构更好并且可复用. 当然,如果使用LINQ, VS提供的智能感知和编译时检查,会让代码减少因为误输入带来的问题.
>“模型定义”函数是一个”组件”,允许你把它当成一个组成部分用在更复杂的表达式中. 这样可以合你代码更简单些,并具可维护性.
>“模型定义”函数能被用在有需要计算的地方,比如一个需要计算的属性,当实体被实例化会带来计算的消耗,不管你用没用到这个属性,而“模型定义”函数只是在你确实用到这个属性时,它才去计算这个属性值.
Entity Framework 6 Recipes 2nd Edition(11-1)译 -> 从“模型定义”函数返回一个标量值的更多相关文章
- Entity Framework 6 Recipes 2nd Edition(11-5)译 -> 从”模型定义”函数返回一个匿名类型
11-5. 从”模型定义”函数返回一个匿名类型 问题 想创建一个返回一个匿名类型的”模型定义”函数 解决方案 假设已有游客(Visitor) 预订(reservation)房间(hotel ) 的模型 ...
- Entity Framework 6 Recipes 2nd Edition 译 -> 目录 -持续更新
因为看了<Entity Framework 6 Recipes 2nd Edition>这本书前面8章的翻译,感谢china_fucan. 从第九章开始,我是边看边译的,没有通读,加之英语 ...
- Entity Framework 6 Recipes 2nd Edition(11-2)译 -> 为一个”模型定义”函数返回一个计算列
11-3. 为一个”模型定义”函数返回一个计算列 问题 想从”模型定义”函数里返回一个计算列 解决方案 假设我们有一个员工(Employee)实体,属性有: FirstName, LastName,和 ...
- Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化
9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...
- Entity Framework 6 Recipes 2nd Edition(9-4)译->Web API 的客户端实现修改跟踪
9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Cod ...
- Entity Framework 6 Recipes 2nd Edition(目录索引)
Chapter01. Getting Started with Entity Framework / 实体框架入门 1-1. A Brief Tour of the Entity Framework ...
- Entity Framework 6 Recipes 2nd Edition(9-1)译->用Web Api更新单独分离的实体
第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程, ...
- Entity Framework 6 Recipes 2nd Edition(13-2)译 -> 用实体键获取一个单独的实体
问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...
- Entity Framework 6 Recipes 2nd Edition(13-3)译 -> 为一个只读的访问获取实体
问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种 ...
随机推荐
- 开源:Taurus.MVC 框架
为什么要创造Taurus.MVC: 记得被上一家公司忽悠去负责公司电商平台的时候,情况是这样的: 项目原版是外包给第三方的,使用:WebForm+NHibernate,代码不堪入目,Bug无限,经常点 ...
- .NET里简易实现AOP
.NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...
- 【SSM框架】Spring + Springmvc + Mybatis 基本框架搭建集成教程
本文将讲解SSM框架的基本搭建集成,并有一个简单demo案例 说明:1.本文暂未使用maven集成,jar包需要手动导入. 2.本文为基础教程,大神切勿见笑. 3.如果对您学习有帮助,欢迎各种转载,注 ...
- iOS之解决崩溃Collection <__NSArrayM: 0xb550c30> was mutated while being enumerated.
崩溃提示:Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <CAL ...
- 敏捷转型历程 - Sprint3 Planning
我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...
- DB2重启数据库实例
DB2重启数据库实例时,有时停止实例会失败,此时需要先确认没有应用链接数据库,然后再关闭数据库实例,并重新启动. 1.查看是否有活动的链接 命令:db2 list applications for d ...
- 从零自学Hadoop(22):HBase协处理器
阅读目录 序 介绍 Observer操作 示例下载 系列索引 本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,Sour ...
- How to accept Track changes in Microsoft Word 2010?
"Track changes" is wonderful and remarkable tool of Microsoft Word 2010. The feature allow ...
- 【一起学OpenFoam】01 OpenFoam的优势
CFD技术发展到今天,已经超过了大半个世纪了,已经涌现出非常多的CFD软件可供人们使用.通用商业CFD软件譬如Fluent.CFX.Star CCM+等在工业上得到了广泛的应用,另外一些专用的软件(如 ...
- BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2050 Solved: 817[Submit][Status ...