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)译 -> 从“模型定义”函数返回一个标量值的更多相关文章

  1. Entity Framework 6 Recipes 2nd Edition(11-5)译 -> 从”模型定义”函数返回一个匿名类型

    11-5. 从”模型定义”函数返回一个匿名类型 问题 想创建一个返回一个匿名类型的”模型定义”函数 解决方案 假设已有游客(Visitor) 预订(reservation)房间(hotel ) 的模型 ...

  2. Entity Framework 6 Recipes 2nd Edition 译 -> 目录 -持续更新

    因为看了<Entity Framework 6 Recipes 2nd Edition>这本书前面8章的翻译,感谢china_fucan. 从第九章开始,我是边看边译的,没有通读,加之英语 ...

  3. Entity Framework 6 Recipes 2nd Edition(11-2)译 -> 为一个”模型定义”函数返回一个计算列

    11-3. 为一个”模型定义”函数返回一个计算列 问题 想从”模型定义”函数里返回一个计算列 解决方案 假设我们有一个员工(Employee)实体,属性有: FirstName, LastName,和 ...

  4. Entity Framework 6 Recipes 2nd Edition(9-3)译->找出Web API中发生了什么变化

    9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Fri ...

  5. Entity Framework 6 Recipes 2nd Edition(9-4)译->Web API 的客户端实现修改跟踪

    9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Cod ...

  6. Entity Framework 6 Recipes 2nd Edition(目录索引)

    Chapter01. Getting Started with Entity Framework / 实体框架入门 1-1. A Brief Tour of the Entity Framework ...

  7. Entity Framework 6 Recipes 2nd Edition(9-1)译->用Web Api更新单独分离的实体

    第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程, ...

  8. Entity Framework 6 Recipes 2nd Edition(13-2)译 -> 用实体键获取一个单独的实体

    问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Paint ...

  9. Entity Framework 6 Recipes 2nd Edition(13-3)译 -> 为一个只读的访问获取实体

    问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种 ...

随机推荐

  1. 开源:Taurus.MVC 框架

    为什么要创造Taurus.MVC: 记得被上一家公司忽悠去负责公司电商平台的时候,情况是这样的: 项目原版是外包给第三方的,使用:WebForm+NHibernate,代码不堪入目,Bug无限,经常点 ...

  2. .NET里简易实现AOP

    .NET里简易实现AOP 前言 在MVC的过滤器章节中对于过滤器的使用就是AOP的一个实现了吧,时常在工作学习中遇到AOP对于它的运用可以说是很熟练了,就是没想过如果自己来实现的话是怎么实现的,性子比 ...

  3. 【SSM框架】Spring + Springmvc + Mybatis 基本框架搭建集成教程

    本文将讲解SSM框架的基本搭建集成,并有一个简单demo案例 说明:1.本文暂未使用maven集成,jar包需要手动导入. 2.本文为基础教程,大神切勿见笑. 3.如果对您学习有帮助,欢迎各种转载,注 ...

  4. iOS之解决崩溃Collection <__NSArrayM: 0xb550c30> was mutated while being enumerated.

    崩溃提示:Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <CAL ...

  5. 敏捷转型历程 - Sprint3 Planning

    我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...

  6. DB2重启数据库实例

    DB2重启数据库实例时,有时停止实例会失败,此时需要先确认没有应用链接数据库,然后再关闭数据库实例,并重新启动. 1.查看是否有活动的链接 命令:db2 list applications for d ...

  7. 从零自学Hadoop(22):HBase协处理器

    阅读目录 序 介绍 Observer操作 示例下载 系列索引 本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,Sour ...

  8. How to accept Track changes in Microsoft Word 2010?

    "Track changes" is wonderful and remarkable tool of Microsoft Word 2010. The feature allow ...

  9. 【一起学OpenFoam】01 OpenFoam的优势

    CFD技术发展到今天,已经超过了大半个世纪了,已经涌现出非常多的CFD软件可供人们使用.通用商业CFD软件譬如Fluent.CFX.Star CCM+等在工业上得到了广泛的应用,另外一些专用的软件(如 ...

  10. BZOJ 3626: [LNOI2014]LCA [树链剖分 离线|主席树]

    3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2050  Solved: 817[Submit][Status ...