zoukankan      html  css  js  c++  java
  • 《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联

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

    5-13  过滤预先加载的实体集合

    问题

      你想过滤预先加载的实体集合,另外,你想使用Code-First来管理数据访问

    解决方案

      实体框架不支持直接使用Include()时过滤关联实体集合,但我们可以通过创建一个匿名类型来完成同样的事情,匿名类型包含实体和要过滤的关联实体集合。

      假设你有如图5-28所示的概念模型

    图5-28 一个包含movies(电影)和它的categories(目录)的模型

     在Visual Studio中添加一个名为Recipe13的控制台应用,并确保引用了实体框架6的库,NuGet可以很好的完成这个任务。在Reference目录上右键,并选择 Manage NeGet Packages(管理NeGet包),在Online页,定位并安装实体框架6的包。这样操作后,NeGet将下载,安装和配置实体框架6的库到你的项目中。

      接下来我们创建两个实体对象:Moviet和Category,复制代码清单5-33中的属性到这三个类中。

    代码清单5-33. 实体类

     public class Category
        {
            public Category()
            {
                Movies = new HashSet<Movie>();
            }
    
            public int CategoryId { get; set; }
            public string Name { get; set; }
            public string ReleaseType { get; set; }
    
            public virtual ICollection<Movie> Movies { get; set; }
        }
    
     public class Movie
        {
            public int MovieId { get; set; }
            public string Name { get; set; }
            public string Rating { get; set; }
            public int CategoryId { get; set; }
    
            public virtual Category Category { get; set; }
        }

    接下来,创建一个名为Recipe13Context的类,并将代码清单5-34中的代码添加到其中,并确保其派生到DbContext类。

    代码清单5-34. 上下文

     1  public class Recipe13Context : DbContext
     2     {
     3         public Recipe13Context()
     4             : base("Recipe13ConnectionString")
     5         {
     6             //禁用实体框架的模型兼容
     7             Database.SetInitializer<Recipe13Context>(null);
     8         }
     9 
    10         public DbSet<Category> Categories { get; set; }
    11         public DbSet<Movie> Movies { get; set; }
    12 
    13         protected override void OnModelCreating(DbModelBuilder modelBuilder)
    14         {
    15             modelBuilder.Entity<Category>().ToTable("Chapter5.Category");
    16             modelBuilder.Entity<Movie>().ToTable("Chapter5.Movie");
    17         }
    18     }

    接下来添加App.Config文件到项目中,并使用代码清单5-35中的代码添加到文件的ConnectionStrings小节下。

    代码清单5-35. 连接字符串

    <connectionStrings>
    <add name="Recipe13ConnectionString"
    connectionString="Data Source=.;
    Initial Catalog=EFRecipes;
    Integrated Security=True;
    MultipleActiveResultSets=True"
    providerName="System.Data.SqlClient" />
    </connectionStrings>

       为了过滤categoryes和与之关联的movies,请按代码清单5-36的方式进行。

     代码清单5-36.过滤预先加载的实体集合

     1  using (var context = new Recipe13Context())
     2             {
     3                 var cat1 = new Category {Name = "Science Fiction", ReleaseType = "DVD"};
     4                 var cat2 = new Category {Name = "Thriller", ReleaseType = "Blu-Ray"};
     5                 var movie1 = new Movie {Name = "Return to the Moon", Category = cat1, Rating = "PG-13"};
     6                 var movie2 = new Movie {Name = "Street Smarts", Category = cat2, Rating = "PG-13"};
     7                 var movie3 = new Movie {Name = "Alien Revenge", Category = cat1, Rating = "R"};
     8                 var movie4 = new Movie {Name = "Saturday Nights", Category = cat1, Rating = "PG-13"};
     9                 context.Categories.Add(cat1);
    10                 context.Categories.Add(cat2);
    11                 context.Movies.Add(movie1);
    12                 context.Movies.Add(movie2);
    13                 context.Movies.Add(movie3);
    14                 context.Movies.Add(movie4);
    15                 context.SaveChanges();
    16             }
    17 
    18             using (var context = new Recipe13Context())
    19             {
    20                 // 通过ReleaseType和Rating过虑
    21                 // 创建匿名类型集合
    22                 var cats = from c in context.Categories
    23                            where c.ReleaseType == "DVD"
    24                            select new
    25                                {
    26                                    category = c,
    27                                    movies = c.Movies.Where(m => m.Rating == "PG-13")
    28                                };
    29 
    30                 Console.WriteLine("PG-13 Movies Released on DVD");
    31                 Console.WriteLine("============================");
    32                 foreach (var cat in cats)
    33                 {
    34                     var category = cat.category;
    35                     Console.WriteLine("Category: {0}", category.Name);
    36                     foreach (var movie in cat.movies)
    37                     {
    38                         Console.WriteLine("	Movie: {0}", movie.Name);
    39                     }
    40                 }
    41             }
    42 
    43             Console.WriteLine("Press <enter> to continue...");
    44             Console.ReadLine();

    代码清单5-36输出如下:

    PG-13 Movies Released on DVD
    ============================
    Category: Science Fiction
            Movie: Return to the Moon
            Movie: Saturday Nights    

    原理

      代码清单5-36,先创建并初始化categoryes和movies。为了保持简短,我们只创建了两个目录和4部电影。

      在查询中,我们创建一个匿名类型的集合,它包含category实例,和在目录中过滤后的movies实例。该查询也过滤了目录集合,只获取发行类型为“DVD"的目录。在示例中,只有一个目录的发行类型为“DVD"。这里我们依赖关系跨度(relationship span)将movies附加到categories。

      这个方法凭借匿名对象帮助我们绕开了预先加载中的限制,预先加载不允许我们过滤预先加载的实体集合。注意,正如前面小节中的示例演示的那样,当我们显式加载时,我们能过虑一个预先加载的实体集合。记住,匿名类型对象的生命周期范围只在创建它的方法中,方法不能返回匿名类型。如果我们目标是返回一个应用后面要处理的实体集,那么我们需要创建一个确切的类型来存放数据,然后将它返回。在我们的示例中,这个确切的类应该是一个简单的类,它只创建两个属性:Category 和一个Movies集合。

    5-14  修改外键关联

    问题

      你想修改外键关联。

    解决方案

      实体框架提供了两种方式来修改外键关联,你可以将一个关联的实体添加到导航属性集合或者赋值给导航属性。你还可以将关联实体的键值设置成外键值。

      假设你有如图5-29所示的模型。

    图5-29 一个包含client和Invoice的模型


       按下面的步骤,使用两种不同的方式,修改client实体和nvoice实体间的外键关联:

        1、右键你的项目,选择Add(添加) ➤ADO.NET Entity Data Model(ADO.NET 实体数据模型)。导入Client和Invoice表。确保勾上选项 Include foreign key columns in the model(包含外键列到模型),它默认是勾选上的。如图5-30所示。这样会从数据库中导入外键关联,它不是多对多关系。

    图5-30 勾上选项 Include foreign key columns in the model(包含外键列到模型)将会为数据中的关系创建外键关联。数据中的关系不是多对多关系。

         2、使用代码清单5-37演示修改外键关联的方法。

    代码清单5-37. 演示修改外键关联

     1  using (var context = new EFRecipesEntities())
     2             {
     3                 var client1 = new Client {Name = "Karen Standfield", ClientId = 1};
     4                 var invoice1 = new Invoice {InvoiceDate = DateTime.Parse("4/1/10"), Amount = 29.95M};
     5                 var invoice2 = new Invoice {InvoiceDate = DateTime.Parse("4/2/10"), Amount = 49.95M};
     6                 var invoice3 = new Invoice {InvoiceDate = DateTime.Parse("4/3/10"), Amount = 102.95M};
     7                 var invoice4 = new Invoice {InvoiceDate = DateTime.Parse("4/4/10"), Amount = 45.99M};
     8 
     9                 // 添加一个invoice到client的导航属性集合Invoices
    10                 client1.Invoices.Add(invoice1);
    11 
    12                 //直接分配一个外键值
    13                 invoice2.ClientId = 1;
    14 
    15                 //使用一个“假”实体附加一存在的行
    16                 context.Database.ExecuteSqlCommand("insert into chapter5.client values (2, 'Phil Marlowe')");
    17                 var client2 = new Client {ClientId = 2};
    18                 context.Clients.Attach(client2);
    19                 invoice3.Client = client2;
    20 
    21                 // 使用Client引用
    22                 invoice4.Client = client1;
    23 
    24                 //保存更改
    25                 context.Clients.Add(client1);
    26                 context.Invoices.Add(invoice2);
    27                 context.Invoices.Add(invoice3);
    28                 context.Invoices.Add(invoice4);
    29                 context.SaveChanges();
    30             }
    31 
    32             using (var context = new EFRecipesEntities())
    33             {
    34                 foreach (var client in context.Clients)
    35                 {
    36                     Console.WriteLine("Client: {0}", client.Name);
    37                     foreach (var invoice in client.Invoices)
    38                     {
    39                         Console.WriteLine("	{0} for {1}", invoice.InvoiceDate.ToShortDateString(),
    40                                           invoice.Amount.ToString("C"));
    41                     }
    42                 }
    43             }
    44 
    45             Console.WriteLine("Press <enter> to continue...");
    46             Console.ReadLine();

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

    Client: Karen Standfield
            4/1/2010 for $29.95
            4/4/2010 for $45.99
            4/2/2010 for $49.95
    Client: Phil Marlowe
            4/3/2010 for $102.95

    原理

      实体框架支持独立关联和外键关联。对于独立关联,由关联间的实体自行跟踪,修改关联的唯一方式是通过修改对象引用

      对于外键关联,你可以通过修改对象引用,或是直接修改外键属性值来修改关联。外键关联不能用作多对多关系

    注意  记住,外键关联简单易用,它是默认方法,也是实体框架开发团队推荐的方法。除非你有具体的业务要求使用独立关联。否则请使用外键关联。

      表5-1展示了独立关联和外键关联之间的主要区别。

    表5-1. 独立关联和外键关联之间的区别

     

     至此第5章结束,下篇我们将进入第六章,感谢你的阅读。觉得有帮助的话,点击下边的推荐以示支持。谢谢!

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

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

  • 相关阅读:
    离散数学期中复习
    计算机组成原理实验_算术逻辑运算器的实现
    数值分析第一章插值方法
    数值分析绪论
    数值分析第三章 常微分方程的差分方法
    数值分析第二章 数值积分
    数据库删除信息后,再次加入信息ID不再从1开始的解决办法
    Codeforces Round #670 (Div. 2)(树的重心,dfs求子树大小)
    Codeforces Round #670 (Div. 2)B. Maximum Product(5个数乘积最大)
    Codeforces Round #668 (Div. 2)A->C
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4525897.html
Copyright © 2011-2022 走看看