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提供的智能感知和编译时检查,会让代码减少因为误输入带来的问题.

>“模型定义”函数是一个”组件”,允许你把它当成一个组成部分用在更复杂的表达式中. 这样可以合你代码更简单些,并具可维护性.

>“模型定义”函数能被用在有需要计算的地方,比如一个需要计算的属性,当实体被实例化会带来计算的消耗,不管你用没用到这个属性,而“模型定义”函数只是在你确实用到这个属性时,它才去计算这个属性值.

附:创建示例用到的数据库的脚本文件

时间: 2024-10-12 14:24:04

Entity Framework 6 Recipes 2nd Edition(11-1)译 -> 从“模型定义”函数返回一个标量值的相关文章

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

11-5. 从”模型定义”函数返回一个匿名类型 问题 想创建一个返回一个匿名类型的”模型定义”函数 解决方案 假设已有游客(Visitor) 预订(reservation)房间(hotel ) 的模型,如Figure 11-5所示. Figure 11-5. A model for hotel reservations 想要返回每位游客房间预订条数和带来的总收入.因为很多地方需要这些信息,所以想要创建一个”模型定义”函数,接受一个查询参数,返回一个包含游客合计信息的匿名类型的集合: 2. 把Li

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

11-3. 为一个”模型定义”函数返回一个计算列 问题 想从”模型定义”函数里返回一个计算列 解决方案 假设我们有一个员工(Employee)实体,属性有: FirstName, LastName,和BirthDate, 如 Figure 11-3所示. Figure 11-3. An Employee entity with a few typical properties 我们想要创建一个”模型定义”函数,让它返回FirstName 和LastName 合并后的full name . 我们想

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

9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Frist实现数据访问管理. 本例,我们模拟一个N层场景,用单独的客户端(控制台应用)来调用单独的基于REST服务的Web网站(WEB API应用) . 注意:每层使用单独的Visual Studio 解决方案, 这样更方便配置.调试和模拟一个N层应用. 假设有一个如Figure 9-3所示的旅行社和预订

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

9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Code First方式实现对数据的访问. 本例,我们模拟一个N层场景,用单独的控制台应用程序作为客户端,调用Web API服务(web api项目). 注:每个层用一个单独的解决方案,这样有助于调试和模拟N层应用. 解决方案 假设我们一个如Figure 9-4.所示模型 Figure 9-4. A 客户

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

Chapter01. Getting Started with Entity Framework / 实体框架入门 1-1. A Brief Tour of the Entity Framework World / 简单浏览实体框架世界 goto1-2. Using Entity Framework / 使用实体框架 Chapter02. Entity Data Modeling Fundamentals / 实体数据建模基础 2-1. Creating a Simple Model2-2. C

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

问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种情况下,你可以通过避开上下文的缓存和修改跟踪来提高代码性能,你可以非常简单地使用AsNoTracking方法来实现. 让我们假设你一个应用程序来管理doctor(医生)的appointments(预约),你的模型如下图Figure 13-5. Figure 13-5. A model for man

Entity Framework 6 Recipes 2nd Edition(13-4)译 -&gt; 有效地创建一个搜索查询

问题 你想用LINQ写一个搜索查询,能被转换成更有效率的SQL.另外,你想用EF的CodeFirst方式实现. 解决方案 假设你有如下Figure 13-6所示的模型 Figure 13-6. A simple model with a Reservation entity            首先,这个例子用EF的CodeFirst方式实现,在Listing 13-10,我们创建实体类Reservation Listing 13-10. The Reservation Entity Obje

Entity Framework 6 Recipes 2nd Edition(13-2)译 -&gt; 用实体键获取一个单独的实体

问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Painting(绘画)类型的实体,如Figure 13-2所示: Figure 13-2. The Painting entity type in our model 在代码In Listing 13-2,我们创建实体类Painting. public class Painting { public str

Entity Framework 6 Recipes 2nd Edition(13-5)译 -&gt; 使POCO的修改追踪更高

问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付)的模型,如Figure 13-7 Figure 13-7. A model with an Account entity and a related Payment   首先,本例用EF的CodeFirst方式实现,在Listing 13-16,我们创建实体类:Account和Payment.为达