zoukankan      html  css  js  c++  java
  • 《Entity Framework 6 Recipes》中文翻译系列 (19) -----第三章 查询之使用位操作和多属性连接(join)

    翻译的初衷以及为什么选择《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. 在查询中使用位操作

     1  static void Main(string[] args)
     2         {
     3             RunExample();
     4         }
     5 
     6         [Flags]
     7         public enum SponsorTypes
     8         {
     9             None = 0,
    10             ContributesMoney = 1,
    11             Volunteers = 2,
    12             IsABoardMember = 4
    13         };
    14 
    15         static void RunExample()
    16         {
    17             using (var context = new EFRecipesEntities())
    18             {
    19                 // 删除之前的测试数据
    20                 context.Database.ExecuteSqlCommand("delete from chapter3.patron");
    21                 // 添加新的测试数据
    22                 context.Patrons.Add(new Patron
    23                 {
    24                     Name = "Jill Roberts",
    25                     SponsorType = (int)SponsorTypes.ContributesMoney
    26                 });
    27                 context.Patrons.Add(new Patron
    28                 {
    29                     Name = "Ryan Keyes",
    30                     //注意位操作符中的OR操作符'|'的用法
    31                     SponsorType = (int)(SponsorTypes.ContributesMoney |
    32                                         SponsorTypes.IsABoardMember)
    33                 });
    34                 context.Patrons.Add(new Patron
    35                 {
    36                     Name = "Karen Rosen",
    37                     SponsorType = (int)SponsorTypes.Volunteers
    38                 });
    39                 context.Patrons.Add(new Patron
    40                 {
    41                     Name = "Steven King",
    42                     SponsorType = (int)(SponsorTypes.ContributesMoney |
    43                                         SponsorTypes.Volunteers)
    44                 });
    45                 context.SaveChanges();
    46             }
    47 
    48             using (var context = new EFRecipesEntities())
    49             {
    50                 Console.WriteLine("Using LINQ...");
    51                 var sponsors = from p in context.Patrons
    52                                //注意位操作符中的AND操作符'&'的用法
    53                                where (p.SponsorType &
    54                                       (int)SponsorTypes.ContributesMoney) != 0
    55                                select p;
    56                 Console.WriteLine("Patrons who contribute money");
    57                 foreach (var sponsor in sponsors)
    58                 {
    59                     Console.WriteLine("	{0}", sponsor.Name);
    60                 }
    61             }
    62 
    63             using (var context = new EFRecipesEntities())
    64             {
    65                 Console.WriteLine("
    Using Entity SQL...");
    66                 var esql = @"select value p from Patrons as p
    67                              where BitWiseAnd(p.SponsorType, @type) <> 0";
    68                 var sponsors = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Patron>(esql,
    69                    new ObjectParameter("type", (int)SponsorTypes.ContributesMoney));
    70                 Console.WriteLine("Patrons who contribute money");
    71                 foreach (var sponsor in sponsors)
    72                 {
    73                     Console.WriteLine("	{0}", sponsor.Name);
    74                 }
    75             }
    76             Console.WriteLine("
    Press <enter> to continue...");
    77             Console.ReadLine();
    78         }

    代码清单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相同的订单。

     1  using (var context = new EFRecipesEntities())
     2             {
     3                 //删除之前的测试数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.[order]");
     5                 context.Database.ExecuteSqlCommand("delete from chapter3.account");
     6                 //添加新的测试数据
     7                 var account1 = new Account { City = "Raytown", State = "MO" };
     8                 account1.Orders.Add(new Order
     9                 {
    10                     Amount = 223.09M,
    11                     ShipCity = "Raytown",
    12                     ShipState = "MO"
    13                 });
    14                 account1.Orders.Add(new Order
    15                 {
    16                     Amount = 189.32M,
    17                     ShipCity = "Olathe",
    18                     ShipState = "KS"
    19                 });
    20 
    21                 var account2 = new Account { City = "Kansas City", State = "MO" };
    22                 account2.Orders.Add(new Order
    23                 {
    24                     Amount = 99.29M,
    25                     ShipCity = "Kansas City",
    26                     ShipState = "MO"
    27                 });
    28 
    29                 var account3 = new Account { City = "North Kansas City", State = "MO" };
    30                 account3.Orders.Add(new Order
    31                 {
    32                     Amount = 102.29M,
    33                     ShipCity = "Overland Park",
    34                     ShipState = "KS"
    35                 });
    36                 context.Accounts.Add(account1);
    37                 context.Accounts.Add(account2);
    38                 context.Accounts.Add(account3);
    39                 context.SaveChanges();
    40             }
    41 
    42             using (var context = new EFRecipesEntities())
    43             {
    44                 var orders = from o in context.Orders
    45                              join a in context.Accounts on
    46                                  // 使用匿名类型来构造一个复合的查询表达式
    47                                  new { Id = o.AccountId, City = o.ShipCity, State = o.ShipState }
    48                                  equals
    49                                  new { Id = a.AccountId, City = a.City, State = a.State }
    50                              select o;
    51 
    52                 Console.WriteLine("Orders shipped to the account's city, state...");
    53                 foreach (var order in orders)
    54                 {
    55                     Console.WriteLine("	Order {0} for {1}", order.AccountId.ToString(),
    56                         order.Amount.ToString());
    57                 }
    58             }
    59 
    60             Console.WriteLine("
    Press <enter> to continue...");
    61             Console.ReadLine();

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

    Orders shipped to the account's city, state...
    Order 31 for $223.09
    Order 32 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/

  • 相关阅读:
    Fidder4 顶部提示 “The system proxy was changed,click to reenable fiddler capture”。
    redis 哨兵 sentinel master slave 连接建立过程
    虚拟点赞浏览功能的大数据量测试
    python基础练习题(题目 字母识词)
    python基础练习题(题目 回文数)
    python基础练习题(题目 递归求等差数列)
    python基础练习题(题目 递归输出)
    python基础练习题(题目 递归求阶乘)
    python基础练习题(题目 阶乘求和)
    python基础练习题(题目 斐波那契数列II)
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4515073.html
Copyright © 2011-2022 走看看