《Entity Framework 6 Recipes》中文翻译系列 (15) -----第三章 查询之与列表值比较和过滤关联实体
翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇
3-8与列表值比较
问题
你想查询一个实体,条件是给定的列表中包含指定属性的值。
解决方案
假设你有如图3-9所示的模型。
图3-9 包含books和它的categoryes的模型
你想查找给定目录列表中的所有图书。在代码清单3-16中使用LINQ和Entity SQL来实现这上功能。
代理清单3-16. 使用LINQ和Entity SQL来查找给定目录列表中的所有图书
using (var context = new EFRecipesEntities())
{
// 删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.book");
context.Database.ExecuteSqlCommand("delete from chapter3.category");
// 添加新的测试数据
var cat1 = new Category {Name = "Programming"};
var cat2 = new Category {Name = "Databases"};
var cat3 = new Category {Name = "Operating Systems"};
context.Books.Add(new Book {Title = "F# In Practice", Category = cat1});
context.Books.Add(new Book {Title = "The Joy of SQL", Category = cat2});
context.Books.Add(new Book
{
Title = "Windows 7: The Untold Story",
Category = cat3
});
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("Books (using LINQ)");
var cats = new List<string> {"Programming", "Databases"};
var books = from b in context.Books
where cats.Contains(b.Category.Name)
select b;
foreach (var book in books)
{
Console.WriteLine("'{0}' is in category: {1}", book.Title,
book.Category.Name);
}
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("\nBooks (using eSQL)");
var esql = @"select value b from Books as b
where b.Category.Name in {'Programming','Databases'}";
var books = ((IObjectContextAdapter) context).ObjectContext.CreateQuery<Book>(esql);
foreach (var book in books)
{
Console.WriteLine("'{0}' is in category: {1}", book.Title,
book.Category.Name);
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();
}
代码清单3-16的输出如下:
Books (using LINQ)
'F# In Practice' is in category: Programming
'The Joy of SQL' is in category: Databases
Books (using ESQL)
'F# In Practice' is in category: Programming
'The Joy of SQL' is in category: Databases
原理
在LINQ查询中,我构造了一个简单的目录名列表,它使用LINQ查询操作符Contains。细心的读者可以注意了,我使用cats集合,并判断它是否包含某一目录名。实体框架将Containts从句转换成SQL语句中的in从句。如代码清单3-17所示:
代码清单3-17. 代码清单3-16中LINQ查询表达式对应的SQL语句
SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[CategoryId] AS [CategoryId]
FROM [chapter3].[Books] AS [Extent1]
LEFT OUTER JOIN [chapter3].[Category] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[CategoryId]
WHERE [Extent2].[Name] IN (N'Programming',N'Databases')
有趣的是,代码清单3-17中的SQL语句,没有为从句的项使用参数。 这不同于LINQ to SQL中产生的代码,列表中的项会被参数化。超过SQL Server的参数限制的风险,会使代码不能运行。
如果我们需要查找出给定目录列表中的所有书,列表目录中包含未分类的目录,我们只需在目录列表中简单地使用null值。产生的SQL语句,如代码清单3-18所示。
代码清单3-18.代码清单3-16中LINQ查询表达式对应的SQL语句,但是目录列表中多了一上null值。
SELECT
[Extent1].[BookId] AS [BookId],
[Extent1].[Title] AS [Title],
[Extent1].[CategoryId] AS [CategoryId]
FROM [chapter3].[Books] AS [Extent1]
LEFT OUTER JOIN [chapter3].[Category] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[CategoryId]
WHERE [Extent2].[Name] IN (N'Programming',N'Databases')
OR [Extent2].[Name] IS NULL
相同地,我们包含了一个使用Entity SQL的查询版本,它显式地包含了一个SQL IN 从句。
3-9过滤关联实体
问题
你想获取一部分,不是全部关联实体。
解决方案
假设你有如图3-10所示的模型。
图3-10 包含Worker和他们的Accidents的模型
在模型中,一个Worker有零个或是多个accidents.每个事故按严重性分类,我们要获取所有的工人,对于与之关联的事故,只对严重事故感兴趣,事故的严重性大于2.
在示例中我们使用了Code-First,在代码清单3-19中,创建了实体类Worker和Accidentd.
代码清单3-19. 实体类
public class Worker
{
public Worker()
{
Accidents = new HashSet<Accident>();
} public int WorkerId { get; set; }
public string Name { get; set; } public virtual ICollection<Accident> Accidents { get; set; }
} public class Accident
{
public int AccidentId { get; set; }
public string Description { get; set; }
public int? Severity { get; set; }
public int WorkerId { get; set; } public virtual Worker Worker { get; set; }
}
接下来,我们在代码清单3-20中创建Code-First中使用的上下文对象。
代码清单3-20. 上下文对象
public class EFRecipesEntities : DbContext
{
public EFRecipesEntities()
: base("ConnectionString") {} public DbSet<Accident> Accidents { get; set; }
public DbSet<Worker> Workers { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Accident>().ToTable("Chapter3.Accident");
modelBuilder.Entity<Worker>().ToTable("Chapter3.Worker");
base.OnModelCreating(modelBuilder);
}
}
使用代码清单3-21中的模式,获取所有的worders,和严重事故。
代码清单3-21 使用CreateSourceQuery和匿名类型获取严重事故
using (var context = new EFRecipesEntities())
{
// 删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.accident");
context.Database.ExecuteSqlCommand("delete from chapter3.worker");
// 添加新的测试数据
var worker1 = new Worker { Name = "John Kearney" };
var worker2 = new Worker { Name = "Nancy Roberts" };
var worker3 = new Worker { Name = "Karla Gibbons" };
context.Accidents.Add(new Accident
{
Description = "Cuts and contusions",
Severity = ,
Worker = worker1
});
context.Accidents.Add(new Accident
{
Description = "Broken foot",
Severity = ,
Worker = worker1
});
context.Accidents.Add(new Accident
{
Description = "Fall, no injuries",
Severity = ,
Worker = worker2
});
context.Accidents.Add(new Accident
{
Description = "Minor burn",
Severity = ,
Worker = worker2
});
context.Accidents.Add(new Accident
{
Description = "Back strain",
Severity = ,
Worker = worker3
});
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
// 显式禁用延迟加载
context.Configuration.LazyLoadingEnabled = false;
var query = from w in context.Workers
select new
{
Worker = w,
Accidents = w.Accidents.Where(a => a.Severity > )
};
query.ToList();
var workers = query.Select(r => r.Worker);
Console.WriteLine("Workers with serious accidents...");
foreach (var worker in workers)
{
Console.WriteLine("{0} had the following accidents", worker.Name);
if (worker.Accidents.Count == )
Console.WriteLine("\t--None--");
foreach (var accident in worker.Accidents)
{
Console.WriteLine("\t{0}, severity: {1}",
accident.Description, accident.Severity.ToString());
}
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();
}
下面是代码清单3-21的输出:
Workers with serious accidents...
John Kearney had the following accidents
Cuts and contusions, severity:
Broken foot, severity:
Nancy Roberts had the following accidents
Minor burn, severity:
Karla Gibbons had the following accidents
--None--
原理
正如你在随后的第五章中看到的那样,我们需要立即加载一个关联集合,我们经常使用Include()方法和一个查询路径。(Include()方法在单个查询中返回父对象和所有的子对象)。然后,Include()方法不允许过滤关联的子实体对象,在本节中,我们展示了,通过轻微的改变,让你可以加载并过滤相关联的子实体对象。
在代码块中,我们创建了一些workers,并分配给它们相关的不同等级的accidents。不得不承认,分配事故给人,有点毛骨悚然!但,这只是为了得到一些可以让我们继续工作的数据。
在随后的查询中,我们获取所有的工人并将结果投射到匿名对象上,这个类型包含了一个worker和一个accidents集合。对于accidents,应该注意,我们是如何过滤集合,只获取严重事故的。
接下来的一行,非常重要!(译注:也就是query.ToList();),我们通过调用ToList()方法,强制查询求值。(记住,LINQ查询默认为延迟加载。意思是,直到结果被使用时,查询才被真正地执行。ToList()方法能强制查询执行)。在Dbcontext(译注:其实是它的子类EFRecipesEntities)中枚举所有的工人和所有的相关的严重事故。 匿名类型不会把accidents附加到workers上,但是通过把它们带到上下文中,实体框架会填充导航属性,将每一个严重事故集合accidents附加到合适的worker上。这个过程一般叫做:Entity Span。这是一个强大而微妙的,发生在实体框架实例化实体类型及它们之间关系的幕后的副作用。
我们关闭了延迟加载,以便让accidents在我们的过滤查询中加载(我们将在第5章讨论延迟加载)。如果打开延迟加载,所有的accidents将在我们引用worker的accidents时才加载。这将导致过虑失败。
我们一旦有了结果集合,就可以枚举并打印出每个worker和它的accidents信息。如果一个worker没有任何严重的accidents,我们打印none来指示他们的安全记录。
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
《Entity Framework 6 Recipes》中文翻译系列 (15) -----第三章 查询之与列表值比较和过滤关联实体的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (11) -----第三章 查询之异步查询
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第三章 查询 前一章,我们展示了常见数据库场景的建模方式,本章将向你展示如何查询实体 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (12) -----第三章 查询之使用SQL语句
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-2使用原生SQL语句更新 问题 你想在实体框架中使用原生的SQL语句,来更新底层 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (13) -----第三章 查询之使用Entity SQL
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-4使用实体SQL查询模型 问题 你想通过执行Entity SQL语句来查询你的实 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-6在查询中设置默认值 问题 你有这样一个用例,当查询返回null值时,给相应属性 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (16) -----第三章 查询之左连接和在TPH中通过派生类排序
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-10应用左连接 问题 你想使用左外连接来合并两个实体的属性. 解决方案 假设你有 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (17) -----第三章 查询之分页、过滤和使用DateTime中的日期部分分组
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-12 分页和过滤 问题 你想使用分页和过滤来创建查询. 解决方案 假设你有如图3 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (18) -----第三章 查询之结果集扁平化和多属性分组
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-14 结果集扁平化 问题 你有一对多关联的两个实体,你想通过一个查询,获取关联 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (19) -----第三章 查询之使用位操作和多属性连接(join)
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-16 过滤中使用位操作 问题 你想在查询的过滤条件中使用位操作. 解决方案 假 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (30) ------ 第六章 继承与建模高级应用之多对多关联
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第六章 继承与建模高级应用 现在,你应该对实体框架中基本的建模有了一定的了解,本章 ...
随机推荐
- 1.Android 视图及View绘制分析笔记之setContentView
自从1983年第一台图形用户界面的个人电脑问世以来,几乎所有的PC操作系统都支持可视化操作,Android也不例外.对于所有Android Developer来说,我们接触最多的控件就是View.通常 ...
- python 学习第二天
由于换了博客,第一篇没有在博客园写,写在了开源中国上,链接地址为http://my.oschina.net/u/254063/blog/719289,大家有兴趣可以看看 一, python 数据类型 ...
- JavaScript isNaN() 函数
定义与用法: isNaN() 函数用于检查其参数是否是非数字值. 语法: isNaN(x) 描述: x是要检测的值. 返回值: 如果 x 是特殊的非数字值 NaN(或者能被转换为这样的值),返 ...
- linpe包-让发送和接收数据分析更快和更容易
1.简介 通常在R中从来进行分析和展现的数据都是以基本的格式保存的,如.csv或者.Rdata,然后使用.Rmd文件来进行分析的呈现.通过这个方式,分析师不仅可以呈现他们的统计分析的结果,还可以直接生 ...
- 16-01-25---Servlet复习笔记(01)
Servlet ServletAPI中有4个java包 javax.servlet 包含Servlet与Servlet容器之间契约的类和接口 javax.servlet.http 包含HT ...
- Euler猜想
这是从http://duodaa.com/blog/index.php/archives/538/截得图,以下是代码 package math; import java.math.BigDecimal ...
- PHP用户注册与登录完整代码【4】
login.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:// ...
- C++:一般情况下,设计函数的形参只需要两种形式
C++:一般情况下,设计函数的形参只需要两种形式.一,是引用形参,例如 void function (int &p_para):二,是常量引用形参,例如 void function(const ...
- linu for循环
用途说明 在shell中用于循环.类似于其他编程语言中的for,但又有些不同.for循环是Bash中最常用的语法结构. 常用格式 格式一 for 变量 do 语句 done 格式二 for 变量 in ...
- android webView开发之js调用java代码示例
1.webView设置 webView.getSettings().setJavaScriptEnabled(true);//设置支持js webView.addJavascriptInterface ...