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

3-14  结果集扁平化

问题

  你有一对多关联的两个实体,你想通过一个查询,获取关联中的两个实体的扁平化投影。扁平化或者叫压缩,这是不规范的叫法。它是指一个有父类和子类的对象图,被投影到一个单独的类中。

解决方案

  假设你有一对拥有一对多关联的实体,如图3-15所示的模型。

图3-15 模型中,一个代表助理的Associate的实体类型和一个代表助理工资历史的AssociateSalary实体

  你想在一个查询中获取所有的associates 和他们的工资历史,可能有些新员工在系统还没有工资记录。 你希望查询结果集中能包含这些关联。

  请按代码清单3-30的方式,查询模型并获取你想要的结果集。

代码清单3-30. 使用LINQ和Entity SQL扁平化结果集

 using (var context = new EFRecipesEntities())
{
// 删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.associatesalary");
context.Database.ExecuteSqlCommand("delete from chapter3.associate");
// 添加新的测试数据
var assoc1 = new Associate { Name = "Janis Roberts" };
var assoc2 = new Associate { Name = "Kevin Hodges" };
var assoc3 = new Associate { Name = "Bill Jordan" };
var salary1 = new AssociateSalary
{
Salary = 39500M,
SalaryDate = DateTime.Parse("8/4/09")
};
var salary2 = new AssociateSalary
{
Salary = 41900M,
SalaryDate = DateTime.Parse("2/5/10")
};
var salary3 = new AssociateSalary
{
Salary = 33500M,
SalaryDate = DateTime.Parse("10/08/09")
};
assoc2.AssociateSalaries.Add(salary1);
assoc2.AssociateSalaries.Add(salary2);
assoc3.AssociateSalaries.Add(salary3);
context.Associates.Add(assoc1);
context.Associates.Add(assoc2);
context.Associates.Add(assoc3);
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("Using LINQ...");
var allHistory = from a in context.Associates
from ah in a.AssociateSalaries.DefaultIfEmpty()
orderby a.Name
select new
{
Name = a.Name,
Salary = (decimal?)ah.Salary,
Date = (DateTime?)ah.SalaryDate
}; Console.WriteLine("Associate Salary History");
foreach (var history in allHistory)
{
if (history.Salary.HasValue)
Console.WriteLine("{0} Salary on {1} was {2}", history.Name,
history.Date.Value.ToShortDateString(),
history.Salary.Value.ToString("C"));
else
Console.WriteLine("{0} --", history.Name);
}
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("\nUsing Entity SQL...");
var esql = @"select a.Name, h.Salary, h.SalaryDate
from Associates as a outer apply
a.AssociateSalaries as h order by a.Name";
var allHistory = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<DbDataRecord>(esql);
Console.WriteLine("Associate Salary History");
foreach (var history in allHistory)
{
if (history["Salary"] != DBNull.Value)
Console.WriteLine("{0} Salary on {1:d} was {2:c}", history["Name"],
history["SalaryDate"], history["Salary"]);
else
Console.WriteLine("{0} --", history["Name"]);
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();
}

  这里的诀窍是,我们“扁平化”(flatten)层次结构的数据,比如,一个associate和多个 salary输入。代码清单3-30输出如下:

 Using LINQ...
Associate Salary History
Bill Jordan Salary on // was $,500.00
Janis Roberts --Kevin Hodges Salary on // was $,500.00
Kevin Hodges Salary on // was $,900.00
Using Entity SQL...
Bill Jordan Salary on // was $,500.00
Janis Roberts --Kevin Hodges Salary on // was $,500.00
Kevin Hodges Salary on // was $,900.00

原理

  为了扁平化结果集,我们使用了3-10中的策略、使用嵌套形式的from从句和DefaultIfEmpty()方法来获得两张表的一个左外连接。方法DefaultIfEmpty()能确保我们有左边表(Associate 实体)的所有行,即使右边(AssociateSalary实体)没有与它对应的行。我们将结果集投影到一个匿名类型,当没有AssociateSalary实体与Associate实体对应时,小心属性salary 和 salarydate得到null值。

  对于Entity SQL 解决方案,我们使用  outer apply 操作符创建Associate实体和AssociateSalary实体之间的匹配。 在SQL Server中,可以使用corss和outer apply操作符。

3-15  使用多属性分组

问题

  你想在一个查询中使用多属性对结果集进行分组,以致在数据库中执行查询时使用多列进行分组。

解决方案

  假设你有包含一个Enent实体的模型,如图3-16所示。Event有属性name,city和sate。你想通过state和city对events进行分组。

图3-16 包含一个Enent实体的模型,Event有属性name,city和sate属性

  在代码清单3-31中,我们使用Code-First方法创建了实体类。

代码清单3-31.实体类型

 public class Event
{
public int EventId { get; set; }
public string Name { get; set; }
public string State { get; set; }
public string City { get; set; }
}

  接下来,代码清单3-32中创建了上下文对象,它是用Code-First方法访问实体框架功能的入口。

代码清单3-32.  上下文对象

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

  使用代码清单3-33中的代码,获取所有的events并按sate和city对结果集分组

代码清单3-33 多属性分组

  using (var context = new EFRecipesEntities())
{
// 删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.event");
//添加新的测试数据
context.Events.Add(new Event
{
Name = "TechFest 2010",
State = "TX",
City = "Dallas"
});
context.Events.Add(new Event
{
Name = "Little Blue River Festival",
State = "MO",
City = "Raytown"
});
context.Events.Add(new Event
{
Name = "Fourth of July Fireworks",
State = "MO",
City = "Raytown"
});
context.Events.Add(new Event
{
Name = "BBQ Ribs Championship",
State = "TX",
City = "Dallas"
});
context.Events.Add(new Event
{
Name = "Thunder on the Ohio",
State = "KY",
City = "Louisville"
});
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("Using LINQ");
var results = from e in context.Events
// 使用匿名类型封闭复合key State 和City
group e by new { e.State, e.City } into g
select new
{
State = g.Key.State,
City = g.Key.City,
Events = g
};
Console.WriteLine("Events by State and City...");
foreach (var item in results)
{
Console.WriteLine("{0}, {1}", item.City, item.State);
foreach (var ev in item.Events)
{
Console.WriteLine("\t{0}", ev.Name);
}
}
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("\nUsing Entity SQL");
var esql = @"select e.State, e.City, GroupPartition(e) as Events
from Events as e
group by e.State, e.City";
var records = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<DbDataRecord>(esql);
Console.WriteLine("Events by State and City...");
foreach (var rec in records)
{
Console.WriteLine("{0}, {1}", rec["City"], rec["State"]);
var events = (List<Event>)rec["Events"];
foreach (var ev in events)
{
Console.WriteLine("\t{0}", ev.Name);
}
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();

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

Using LINQ
Events by State and City...
Louisville, KY
Thunder on the Ohio
Raytown, MO
Little Blue River Festival
Fourth of July Fireworks
Dallas, TX
TechFest
BBQ Ribs Championship
Using Entity SQL
Events by State and City...
Louisville, KY
Thunder on the Ohio
Raytown, MO
Little Blue River Festival
Fourth of July Fireworks
Dallas, TX
TechFest
BBQ Ribs Championship

原理

   在代码清单3-33中,针对这个问题,展示了两种不同的方法。 第一种方法使用LINQ和group by 操作符按sate和city对结果集进行分组。当用group by进行多属性分组时, 我们创建了一个匿名类型对数据进行分组。 使用into从句将分组放到g中,它是我们存放查询结果集的第二个序列。

  我们把结果集从g中投影到第二个匿名类型中,通过从分组key的字段State(第一个匿名类型中)获取State值,从分组key的字段City中获取 City值,对于events 我只是简单地把分组的全部成员分配给它。

  对于Entity SQL方法,我们只能投影group by 从句使用的列、常量或者从聚合函数计算得到的值。在我们示例中,我们投影state、city和分组中的events.

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

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

《Entity Framework 6 Recipes》中文翻译系列 (18) -----第三章 查询之结果集扁平化和多属性分组的更多相关文章

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

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

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

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

  3. 《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句

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

  4. 《Entity Framework 6 Recipes》中文翻译系列 (13) -----第三章 查询之使用Entity SQL

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

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

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-6在查询中设置默认值 问题 你有这样一个用例,当查询返回null值时,给相应属性 ...

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

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

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

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

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

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

  9. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

随机推荐

  1. Node.js之NPM工具使用

    1.NPM介绍:包管理工具 (1)允许用户从NPM服务器下载别人编写的第三方包到本地石使用 (2)允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用 (3)允许用户将自己编写的包或命令行程 ...

  2. cocoapods导入shareSDK分享实现

    这个在官方文档没有说这两个库是必须导入的,但是不导入是会有错误的 pod 'ShareSDK3/ShareSDKConfigurationFile' pod 'ShareSDK3/ShareSDKEx ...

  3. 安卓四核PDA手持PDA智能POS机 打印二维码 分享

    很多项目都会用到 类似的要求  移动手持终端 通过程序 可以生成条码或二维码 打印出小票或标签纸 下面直接上代码 希望对大家有点用处 private void print(){ csys.setTex ...

  4. 1.Linux常用命令

    命令名称:ls 格式: ls  [-选项] [参数]  注:中括号表示可选 命令路径:/bin/ls 功能描述:显示信息 例如: 选项 -a 显示所有的文件,包括隐藏文件,(以 . 开头的文件) -l ...

  5. JAVA正则表达式中如何匹配反斜杠 \

    有时候我们需要匹配反斜杠,你可能会把对应的正则表达式写成 "\\" 然后可能会有如下输出: Exception in thread "main" java.ut ...

  6. [软件推荐]快速文件复制工具(Limit Copy) V4.0 绿色版

    快速文件复制工具(Limit Copy)绿色版是一款智能变频超快复制绿色软件. 快速文件复制工具(Limit Copy)功能比较完善,除了文件复制还可以智能变频,直接把要复制的文件拖入窗口即可,无需手 ...

  7. Curator leader 选举(一)

    要想使用Leader选举功能,需要添加recipes包,可以在maven中添加如下依赖: <dependency> <groupId>org.apache.curator< ...

  8. 阅读jquery源码与js依赖加载的模块化!

    阅读源码肯定是先下载有注释的源码 我也是醉了,10309 行代码,在陆续续的一个月之内,看完了,虽有收获但收获不大, 直到又一次看jquery的github,怎么会有cmd????没听过使用jquer ...

  9. c# 局域网文件传输实例

    一个基于c#的点对点局域网文件传输小案例,运行效果截图 //界面窗体 using System;using System.Collections.Generic;using System.Compon ...

  10. 如何去掉dede列表推荐时标题被加粗

    dede在列表推荐文章默认为加粗不清楚的可以看图: 那个加黑的是默认的.如果你不想要被加黑,可以做如下改动.在include里找到文件:arc.listview.class.php查找并删除(注释掉也 ...