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

3-16  过滤中使用位操作

问题

  你想在查询的过滤条件中使用位操作。

解决方案

  假设你有一个实体类型,它有一个你想用来做位标识的整型属性。你将使用这个属性中的bit位来表示实体中特殊属性存在与否(译注:作者想表达的是,bit中位为0或1时,实体的类型就会不一样)。例如,假设你有一个表示当地画廊的赞助者(patrons)实体,一些赞助者直接捐款(contribute money),一些在画廊里当志愿者(volunteer),一些服务于董事会(board of directors)。一些赞助者不止提供一种方式来赞助画廊。一个包含此实体的模型,如图3-17所示。

图3-17  实体类型Patron,有一个SponsorType属性,它被用作一个用来指示Patron赞助类型的位标识集合

  我们想通过赞助者(patron)提供的赞助类型来过滤查询。按代码清单3-34来实现我们的要求。

代码清单3-34. 在查询中使用位操作

  static void Main(string[] args)
{
RunExample();
} [Flags]
public enum SponsorTypes
{
None = ,
ContributesMoney = ,
Volunteers = ,
IsABoardMember =
}; static void RunExample()
{
using (var context = new EFRecipesEntities())
{
// 删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.patron");
// 添加新的测试数据
context.Patrons.Add(new Patron
{
Name = "Jill Roberts",
SponsorType = (int)SponsorTypes.ContributesMoney
});
context.Patrons.Add(new Patron
{
Name = "Ryan Keyes",
//注意位操作符中的OR操作符'|'的用法
SponsorType = (int)(SponsorTypes.ContributesMoney |
SponsorTypes.IsABoardMember)
});
context.Patrons.Add(new Patron
{
Name = "Karen Rosen",
SponsorType = (int)SponsorTypes.Volunteers
});
context.Patrons.Add(new Patron
{
Name = "Steven King",
SponsorType = (int)(SponsorTypes.ContributesMoney |
SponsorTypes.Volunteers)
});
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("Using LINQ...");
var sponsors = from p in context.Patrons
//注意位操作符中的AND操作符'&'的用法
where (p.SponsorType &
(int)SponsorTypes.ContributesMoney) !=
select p;
Console.WriteLine("Patrons who contribute money");
foreach (var sponsor in sponsors)
{
Console.WriteLine("\t{0}", sponsor.Name);
}
} using (var context = new EFRecipesEntities())
{
Console.WriteLine("\nUsing Entity SQL...");
var esql = @"select value p from Patrons as p
where BitWiseAnd(p.SponsorType, @type) <> 0";
var sponsors = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Patron>(esql,
new ObjectParameter("type", (int)SponsorTypes.ContributesMoney));
Console.WriteLine("Patrons who contribute money");
foreach (var sponsor in sponsors)
{
Console.WriteLine("\t{0}", sponsor.Name);
}
}
Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();
}

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

Using LINQ...
Patrons who contribute money
Jill Roberts
Ryan Keyes
Steven King
Using Entity SQL...
Patrons who contribute money
Jill Roberts
Ryan Keyes
Steven King

原理

  在我们的模型中,实体类型Patron,将多个位标识打包在一个单独的整形属性中。一个赞助者(patron)可以用多种方式赞助(sponsor)画廊。每种赞助类型用SponsorType属性中的不同的位来表示,我们可以创建一个enum类型来表示每种赞助方式。我们为每种类型分配2的整数幂作为它的值。这意味中每个类型在SponsorType属性中都有确定的一个位。(译注:整型在C#中占用32位bit,2的二进制表示为 00000000000000000000000000000010,它在示例中表示 志愿者(Volunteers),4的二进制表示为00000000000000000000000000000100,它在示例中表示 董事会成员(IsABoardMember))。

  当插入patrons时,我们分配赞助类型给SponsorType属性,对于不止一种方式(类型)赞助画廊的赞助者,我们简单地使用OR操作符(|)将不同的方式合并起来。

  对于LINQ查询,我们使用了AND位操作符(&),从SponsorType属性值中提取ContributesMoney(捐钱)这种赞助方式的位。如果结果为非零,那么这个赞助者就有ContributesMoney标识。如果我们想查询不止一种赞助方式(类型)的赞助者,得在我们使用位操作符AND(&)来提取标识位之前,使用OR来连接所有我们感兴趣的SponsorType.

  第二种方法演示了,使用Entity SQL的方式。我们使用函数BitWiseAnd()来提取标识位。Entity SQL支持完整的位操作函数。

3-17  多列连接(Join)

问题

  你想通过多个属性来连接(join)两个实体。

解决方案

  假设你有一个如图3-18所示的模型。Account(账户)实体类型与Order(订单)实体类型有一对多关联。每个账户可能有多个订单,然而,一个订单只能关联到一个确切的账户上。你想查找所有的快递到与账号的city,state相同的订单。

图3-18 一个包含Account实体类型和与之关联的Order实体的模型

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

代码清单3-35. 实体类型Account和Order

 public class Account
{
public Account()
{
Orders = new HashSet<Order>();
} public int AccountId { get; set; }
public string City { get; set; }
public string State { get; set; }
public virtual ICollection<Order> Orders { get; set; }
} public class Order
{
public int OrderId { get; set; }
public Decimal Amount { get; set; }
public int AccountId { get; set; }
public string ShipCity { get; set; }
public string ShipState { get; set; }
public virtual Account Account { get; set; }
}

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

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

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

  使用代码清单3-37查找订单。

代码清单3-37. 使用多属性连接(Join)来查找所有快递到与账号的City和State相同的订单。

  using (var context = new EFRecipesEntities())
{
//删除之前的测试数据
context.Database.ExecuteSqlCommand("delete from chapter3.[order]");
context.Database.ExecuteSqlCommand("delete from chapter3.account");
//添加新的测试数据
var account1 = new Account { City = "Raytown", State = "MO" };
account1.Orders.Add(new Order
{
Amount = 223.09M,
ShipCity = "Raytown",
ShipState = "MO"
});
account1.Orders.Add(new Order
{
Amount = 189.32M,
ShipCity = "Olathe",
ShipState = "KS"
}); var account2 = new Account { City = "Kansas City", State = "MO" };
account2.Orders.Add(new Order
{
Amount = 99.29M,
ShipCity = "Kansas City",
ShipState = "MO"
}); var account3 = new Account { City = "North Kansas City", State = "MO" };
account3.Orders.Add(new Order
{
Amount = 102.29M,
ShipCity = "Overland Park",
ShipState = "KS"
});
context.Accounts.Add(account1);
context.Accounts.Add(account2);
context.Accounts.Add(account3);
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
var orders = from o in context.Orders
join a in context.Accounts on
// 使用匿名类型来构造一个复合的查询表达式
new { Id = o.AccountId, City = o.ShipCity, State = o.ShipState }
equals
new { Id = a.AccountId, City = a.City, State = a.State }
select o; Console.WriteLine("Orders shipped to the account's city, state...");
foreach (var order in orders)
{
Console.WriteLine("\tOrder {0} for {1}", order.AccountId.ToString(),
order.Amount.ToString());
}
} Console.WriteLine("\nPress <enter> to continue...");
Console.ReadLine();

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

Orders shipped to the account's city, state...
Order for $223.09
Order for $99.29

原理

  为了解决这个问题,你可以先查找出所有的accounts,然后通过比较Orders集合中的每个订单,并找出与account的state和city一样的订单。对于只有少量account的情况,这可能是一个合理的解决方案。但是,一般情况下,最好的解决方案是,把这类处理放在存储层去,因为在存储层会更有效率。

  一开始,Account和Order通过AccountId属性连接在一起,然而,在这个解决方案中,我们通过在equals从句的两边分别创建一个匿名类型明确地形成一个连接(Join)。连接(Join)实体的属性数量多于一个时,需要用到匿名构造。 我们要确保两边的匿名类型是相同的,必须要有相同的属性,相同属性定义顺序。这里,我们明确地在数据库中的两张表间创建了一个内连接(inner-join),意味着,因为连接条件,寄往别cities和state的orders将不会包含在结果中。

  至此,第三章就到此结束。下一篇我们将进行第四章的学习。感谢你的阅读和学习。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. linux系统swappiness参数在内存与交换分区间优化

    http://blog.itpub.net/29371470/viewspace-1250975        swappiness的值的大小对如何使用swap分区是有着很大的联系的.swappine ...

  2. 数据结构0103汉诺塔&八皇后

    主要是从汉诺塔及八皇后问题体会递归算法. 汉诺塔: #include <stdio.h> void move(int n, char x,char y, char z){ if(1==n) ...

  3. InnoDB还是MyISAM 再谈MySQL存储引擎的选择

    两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁.而MyISAM不支持.所以MyISAM往往就容易被人认为只适合在小项目中使用. 我作为使用MySQL的用户角度出发,Innodb和My ...

  4. js 处理字母 大小写的 一些函数

    js中实现字母大小写转换主要用到了四个js函数: 1.toLocaleUpperCase2.toUpperCase3.toLocaleLowerCase4.toLowerCase 下面就这四个实现大小 ...

  5. JAVA安装过程中出现的“javac不是内部或外部指令”的解决方法

    近来重新安装了JAVA,安装过程中出现问题,网上找到解决办法,汇总发布. 解决流程: 1.确定自己的环境变量设置没问题,没有出现遗漏 : . 等情况 (具体环境变量设置百度) 2.环境变量设置后 ,d ...

  6. java运行时内存模式学习

    学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoa ...

  7. Unity3d:UI面板管理整合进ToLua

    本文基于 https://github.com/chiuan/TTUIFramework https://github.com/jarjin/LuaFramework_UGUI 进行的二次开发,Tha ...

  8. 图文解释XCode常用快捷键的使用

    刚开始用Xcode是不是发现以前熟悉的开发环境的快捷键都不能用了?怎么快捷运行,停止,编辑等等.都不一样了.快速的掌握这些快捷键,能提供开发的效率. 其实快捷键在Xcode的工具栏里都标注有,只是有的 ...

  9. MongoDB常用命令

    本文整理了一年多以来我常用的MongoDB操作,涉及mongo-shell.pymongo,既有运维层面也有应用层面,内容有浅有深,这也就是我从零到熟练的历程. MongoDB的使用之前也分享过一篇, ...

  10. sequelize常见操作使用方法

    关于sequelize的准备工作这里不再赘述. 一.引入sequelize模块 var Sequelize = require('sequelize'); 二.连接数据库 var sequelize  ...