《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集

时间:2022-12-19 09:14:22

翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

3-6在查询中设置默认值

问题

  你有这样一个用例,当查询返回null值时,给相应属性设置默认值。在我们示例中,当数据库中返回null值时,用‘0’作为YearsWorked属性的默认值。

解决方案

  假设你有如图3-7所示的模型,你想通过模型查询employees。在数据库中,代表employees的表包含一可为空的YearsWorked列。该列映射到Employee实体中的YearsWorked属性。你想把返加行中包含null值的YearsWorked设置成默认值0。

《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集

图3-7 包含一个Employee实体类型的模型,实体类型包含一个EmployeeId属性、一个Name属性和一个YearsWorked属性

  示例使用实体框架中的code-first,在代码清单3-11,我们创建了一个Employee类。

代码清单3-11. Employee 实体类

   public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public int? YearsWorked { get; set; }
}

接下来,代码清单3-12创建上下文对象

  public class EFRecipesEntities : DbContext
{
public EFRecipesEntities()
: base("ConnectionString") {} public DbSet<Employee> Employees { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>().ToTable("Chapter3.Employee");
base.OnModelCreating(modelBuilder);
}
}

  因为我们使用的是Code-First的方式,所以能以代码清单3-13所示的方式设值默认值。注意,代码清单3-13中的方式不能真正的实现(从数据库中返回)Employee实体类型的实例的默认值。相反,查询的结果是一个匿名类型的集合,当数据库表中YearsWorked列值为null时,匿名类型的属性YearsWorked被以编程的方式设置成0.因此,数据库中相应的列仍然保持null值,但在实体框架的结果集中我们使用0作为它的默认值。记住,代码清单3-13所示的匿名类型,它是一个在运行时,依据new关键字后边大括号内的属性动态创建的类。

代码清单3-13. 使用LINQ和Entity SQL给Null值填充默认值

 using (var context = new EFRecipesEntities())
{
// 删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.employee");
// 添加新的测试数据
context.Employees.Add(new Employee
{
Name = "Robin Rosen",
YearsWorked =
});
context.Employees.Add(new Employee {Name = "John Hancock"});
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("Employees (using LINQ)");
var employees = from e in context.Employees
select new { Name = e.Name, YearsWorked = e.YearsWorked ?? };
foreach (var employee in employees)
{
Console.WriteLine("{0}, years worked: {1}", employee.Name,
employee.YearsWorked);
}
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("\nEmployees (using ESQL w/named constructor)");
var esql = @"select value Recipe3_6.Employee(e.EmployeeId,
e.Name,
case when e.YearsWorked is null then 0
else e.YearsWorked end)
from Employees as e"; var employees = ((IObjectContextAdapter) context).ObjectContext.CreateQuery<Employee>(esql);
foreach (var employee in employees)
{
Console.WriteLine("{0}, years worked: {1}", employee.Name,
employee.YearsWorked.ToString());
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();

代码清单3-13的输出如下:

Employees (using LINQ)
Robin Rosen, years worked:
John Hancock, years worked:
Employees (using ESQL w/named constructor)
Robin Rosen, years worked:
John Hancock, years worked:

原理

  我们在这里使用的方法是,使用LINQ和eSQL将结果投影到一个匿名类型集合,当YearsWorked在底层数据库中为null值时,查询将其设置成0。

  在LINQ方法中,我们使用了C#中的 null值合并(null-coalescing) 操作符??,当YearsWorded在数据库中的值为null时,将0分配给它。我们将结果投影到一个匿名类型集合。

  在Entity SQL方法中,当YearsWorded在数据库中的值为null时,我们使用了case语句来分配0给YearsWorked。我们这里演示了,如何使用Entity SQL,在不设置默认值的情况下,实例化Employee实体类型的实例。为此,我们使用了实体类型的命名构造函数(named  constructor)。这个构造函数,使用实体类型中属性定义的顺序从参数中为属性赋值。在我们示例中,Employee实体的属性定义顺序为:EmployeeId,Name,YearsWorked,从eSQL 查询中传递给构造函数参数的顺序与此一至。不幸的是,在LINQ to Entiytes中没有合适的命名构造函数语法支持。

3-7从存储过程中返回多结果集

问题

  你有一个存储过程,它返回多个结果集。你想从每个结果集实体化到实体实例。

解决方案

  假设你有如图3-8所示的模型和一个代码清单3-14所示的存储过程,存储过程返回job和bid集合。

《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集

图3-8 一个代码job和bid的模型

 代码清单3-14. 返回多结果集的存储过程

 create procedure Chapter3.GetBidDetails
as
begin
select * from Chapter3.Job
select * from Chapter3.Bid
end

  在我们的模型中,每个job有零个或是多个bids。我们的存储过程返回所有的jobs和bids。我们想执行存储过程并实例化两个结果集中的所有jobs和bids。按代码清单3-15实现此需求。

代码清单3-15. 从存储过程返回的多结果集实例化Jobs和Bids

  using (var context = new EFRecipesEntities())
{
var job1 = new Job {JobDetails = "Re-surface Parking Log"};
var job2 = new Job {JobDetails = "Build Driveway"};
job1.Bids.Add(new Bid {Amount = 948M, Bidder = "ABC Paving"});
job1.Bids.Add(new Bid {Amount = 1028M, Bidder = "TopCoat Paving"});
job2.Bids.Add(new Bid {Amount = 502M, Bidder = "Ace Concrete"});
context.Jobs.Add(job1);
context.Jobs.Add(job2);
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
var cs = @"Data Source=.;Initial Catalog=EFRecipes;Integrated Security=True";
var conn = new SqlConnection(cs);
var cmd = conn.CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "Chapter3.GetBidDetails";
conn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
var jobs = ((IObjectContextAdapter) context).ObjectContext.Translate<Job>(reader, "Jobs",
MergeOption.AppendOnly).ToList();
reader.NextResult();
((IObjectContextAdapter) context).ObjectContext.Translate<Bid>(reader, "Bids", MergeOption.AppendOnly)
.ToList();
foreach (var job in jobs)
{
Console.WriteLine("\nJob: {0}", job.JobDetails);
foreach (var bid in job.Bids)
{
Console.WriteLine("\tBid: {0} from {1}",
bid.Amount.ToString(), bid.Bidder);
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();
}

代码清单3-15输出如下:

Job: Re-surface Parking Log
Bid: $948.00 from ABC Paving
Bid: $,028.00 from TopCoat Paving
Job: Build Driveway
Bid: $502.00 from Ace Concrete

原理

  一开始,我添加了两个jobs和一些与之相对的bids,然后将他们添加到上下文中,最新调用SaveChanges()函数保存至数据库。

  实体框架5.0就已经提供了对存储过程返回多结果集的支持。然后,要使用此功能的话,你得使用遗留的ObjectContext对象,因为最新的DbContext对象对此不提供直接的支持。 为了解决这个问题,我们使用了SqlClient方式来读取存储过程的返回。此模式需要创建SqlConnection,SqlCommand.将存储过程的名称设置成SqlCommand的命令文本,最后调用ExecuteReader()方法得到一个DataReader对象。

  有了reader对象后,我们就可以使用ObjectContext对象中的Translate()方法从reader对象中实例化Job实体。这个方法需要以下三个参数:reader,实体集的名称和一个合并选项。需要实体集名称是因为,一个实体可能存在包含多个实体集的结果集中。实体框架需要知道你想使用哪个实体集。

  合并选项有一些需要注意的地方,我们使用MergeOption.AppendOnly选项,会让实体的实例被添加到上下文对象中并被跟踪。使用这个选项的原因是,让实体框架自动关联jobs和bids。为了实现这个目的,只要简单地把jobs和bids添加到上下文中,实体框架就会帮我们自动关联它们。这为我们省去了大量的冗余代码。

  方法Translate()的另一个简单点的版本,不需要MergeOption. 它将离开上下文对象来实例化对象。这两个版本的方法略有不同,在上下文对象之外创建的对象将不被跟踪。如果你使用这个简单版本的Translate()方法读取jobs,那么你将不能在上下文中实例化一个新的bits。因为实体框架没有任何有关jobs关联对象的引用。这些jobs是在上下文对象之外创建的。另外你不能修改这些实例的属性并期待实体框架帮你保存这些改变。

  我们使用ToList()方法强制枚举每个查询,这是因为Translate()方法返回的是ObjectResult<T>,它不会真正的从reader中读取结果。我需要在使用NextResult()方法处理下一个结果集前,强制从reader中读取结果。在实践中,我们多数会在代码中使用NextResult()方法来继续查找存储过程返回的结果集。

  虽然我们没有在这个示例中见到它,但需要引起注意的是,Translate()方法绕过了映射层模型。如果你想使用继承映射,或者使用一个包含复合类型属性的实体,Translate()方法会失败。Translate()方法需要DbDataReader对象提供与实体属性匹配的列。匹配过程简单地使用名称进行。如果一列名不能与一个属性匹配上,Translate()方法也会失败。

实体框架交流QQ群:  458326058,欢迎有兴趣的朋友加入一起交流

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集的更多相关文章

  1. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;11&rpar; -----第三章 查询之异步查询

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...

  2. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;12&rpar; -----第三章 查询之使用SQL语句

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-2使用原生SQL语句更新 问题 你想在实体框架中使用原生的SQL语句,来更新底层 ...

  3. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;13&rpar; -----第三章 查询之使用Entity SQL

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-4使用实体SQL查询模型 问题 你想通过执行Entity SQL语句来查询你的实 ...

  4. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;15&rpar; -----第三章 查询之与列表值比较和过滤关联实体

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-8与列表值比较 问题 你想查询一个实体,条件是给定的列表中包含指定属性的值. 解 ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;16&rpar; -----第三章 查询之左连接和在TPH中通过派生类排序

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-10应用左连接 问题 你想使用左外连接来合并两个实体的属性. 解决方案 假设你有 ...

  6. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;17&rpar; -----第三章 查询之分页、过滤和使用DateTime中的日期部分分组

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-12 分页和过滤 问题 你想使用分页和过滤来创建查询. 解决方案 假设你有如图3 ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;18&rpar; -----第三章 查询之结果集扁平化和多属性分组

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-14  结果集扁平化 问题 你有一对多关联的两个实体,你想通过一个查询,获取关联 ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;19&rpar; -----第三章 查询之使用位操作和多属性连接&lpar;join&rpar;

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-16  过滤中使用位操作 问题 你想在查询的过滤条件中使用位操作. 解决方案 假 ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 &lpar;20&rpar; -----第四章 ASP&period;NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

随机推荐

  1. 判断安卓和IOS

    var u = navigator.userAgent; var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > - ...

  2. &lbrack;Server&rsqb; 搭建发布环境Web Deploy

    在Web Deploy前,实际上完全可以使用FTP方式发布网站. 如果你购买的只是虚拟空间,那FTP方式更广泛. 先来搭建一套FTP吧 添加FTP服务 在网站上添加FTP发布 配置FTP服务 FTP注 ...

  3. 你必须掌握的Java基础:JSON解析工具-json-lib

    一.简介  json-lib是一个Java类库,提供将Java对象,包括beans,maps,collections,java arrays和xml等转换成JSON,或者反向转换的功能. 二.准备 在 ...

  4. &lpar;Android&rpar;Wifi-Direct直连

    因项目需要Pad端和手机端交互,采用wifi直连.查阅资料,大概写下一些资料和收获吧.注:大公司的代码带不出来,我也比较懒不想再认真去写一遍了,所以大概这个意思哦. wifi直连也叫做wifi设备点对 ...

  5. STM32之使用库函数驱动LED灯

    一.熟悉GPIO结构体 以下这个结构体是我从官方手册中获取的: typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMod ...

  6. dos 批量重命名 bat

    @echo off setlocal enabledelayedexpansion echo %var% set /a i = i + var for %%x in (*) do ( if not & ...

  7. 查看Andorid应用是32位还是64位

    adb shell cat /proc/进程pid/maps 查看linker位数即可

  8. ACM题目————A Knight&&num;39&semi;s Journey

    Description BackgroundThe knight is getting bored of seeing the same black and white squares again a ...

  9. 洛谷P4248 &lbrack;AHOI2013&rsqb;差异&lpar;后缀自动机求lcp之和&rpar;

    题目见此 题解:首先所有后缀都在最后一个np节点,然后他们都是从1号点出发沿一些字符边到达这个点的,所以下文称1号点为根节点,我们思考一下什么时候会产生lcp,显然是当他们从根节点开始一直跳相同节点的 ...

  10. Java学习(JDBC java连接数据库)

    一.概述 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写 ...