第五章 加载实体和导航属性之查询内存对象

5-4 查询内存对象

问题

你想使用模型中的实体对象,如果他们已经加载到上下文中,便不用与数据库发生交互。另外,你想使用Code-First来管理数据访问。

解决方案

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

图5-12 一个包含Club实体对象的简单模型

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

创建一个名为Club类,复制代码清单5-8中的属性到这个类中,创建club实体。

代码清单5-8. Club实体类

public class Club
{
    public int ClubId { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
}

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

代码清单5-9. 上下文对象Recipe4Contex

public class Recipe4Context : DbContext
{
    public Recipe4Context()
        : base("Recipe4ConnectionString")
    {
        // 禁用实体框架的模型兼容性
        Database.SetInitializer<Recipe3Context>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
         modelBuilder.Entity<Club>().ToTable("Chapter5.Club");
    }

    public DbSet<Club> Clubs { get; set; }
}

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

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


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



在这个模型中,我们有一个实体类型Club,你可以通过查询获取各种各样的俱乐部(Clubs). 我们可以通过查询DbSet的属性Local来减少与数据库的交互。它是对Club实体的包装。属性Local 公布了一个内存实体对象的动态集合(Observable Collection),与上下文对象保持同步。代码清单5-11演示了Local动态集合的用法。

代码清单5-11. DbSet对象中Local 属性的一般用法

     int desertSunId;

     using (var context = new Recipe4Context())
     {
         var starCity = new Club {Name = "Star City Chess Club", City = "New York"};
         var desertSun = new Club {Name = "Desert Sun Chess Club", City = "Phoenix"};
         var palmTree = new Club {Name = "Palm Tree Chess Club", City = "San Diego"};

         context.Clubs.Add(starCity);
         context.Clubs.Add(desertSun);
         context.Clubs.Add(palmTree);

         context.SaveChanges();

         desertSunId = desertSun.ClubId;
     }

     using (var context = new Recipe4Context())
     {
         Console.WriteLine("\nLocal Collection Behavior");
         Console.WriteLine("=================");

         Console.WriteLine("\nNumber of Clubs Contained in Local Collection: {0}", context.Clubs.Local.Count);
         Console.WriteLine("=================");

         Console.WriteLine("\nClubs Retrieved from Context Object");
         Console.WriteLine("=================");
         foreach (var club in context.Clubs.Take(2))
         {
             Console.WriteLine("{0} is located in {1}", club.Name, club.City);
         }

         Console.WriteLine("\nClubs Contained in Context Local Collection");
         Console.WriteLine("=================");
         foreach (var club in context.Clubs.Local)
         {
             Console.WriteLine("{0} is located in {1}", club.Name, club.City);
         }

         context.Clubs.Find(desertSunId);

         Console.WriteLine("\nClubs Retrieved from Context Object - Revisted");
         Console.WriteLine("=================");
         foreach (var club in context.Clubs)
         {
             Console.WriteLine("{0} is located in {1}", club.Name, club.City);
         }

         Console.WriteLine("\nClubs Contained in Context Local Collection - Revisted");
         Console.WriteLine("=================");
         foreach (var club in context.Clubs.Local)
         {
             Console.WriteLine("{0} is located in {1}", club.Name, club.City);
         }

         //获取local集合的引用
         var localClubs = context.Clubs.Local;

         // 添加一个新的 Club
         var lonesomePintId = -999;
         localClubs.Add(new Club
             {
                 City = "Portland",
                 Name = "Lonesome Pine",
                 ClubId = lonesomePintId
             });

         // 删除 Desert Sun club
         localClubs.Remove(context.Clubs.Find(desertSunId));

         Console.WriteLine("\nClubs Contained in Context Object - After Adding and Deleting");
         Console.WriteLine("=================");
         foreach (var club in context.Clubs)
         {
             Console.WriteLine("{0} is located in {1} with a Entity State of {2}",
                               club.Name, club.City, context.Entry(club).State);
         }

         Console.WriteLine("\nClubs Contained in Context Local Collection - After Adding and Deleting");
         Console.WriteLine("=================");
         foreach (var club in localClubs)
         {
             Console.WriteLine("{0} is located in {1} with a Entity State of {2}",
                               club.Name, club.City, context.Entry(club).State);
         }

         Console.WriteLine("\nPress <enter> to continue...");
         Console.ReadLine();

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

Local Collection Behavior
=================
Number of Clubs Contained in Local Collection: 0
=================
Clubs Retrieved from Context Object
=================
Star City Chess Club is located in New York
Desert Sun Chess Club is located in Phoenix
Clubs Contained in Context Local Collection
=================
Star City Chess Club is located in New York
Desert Sun Chess Club is located in Phoenix
Clubs Retrieved from Context Object - Revisted
=================
Star City Chess Club is located in New York
Desert Sun Chess Club is located in Phoenix
Palm Tree Chess Club is located in San Diego
Clubs Contained in Context Local Collection - Revisted
=================
Star City Chess Club is located in New York
Desert Sun Chess Club is located in Phoenix
Palm Tree Chess Club is located in San Diego
Clubs Contained in Context Object – After Adding and Deleting
=================
Star City Chess Club is located in New York with a Entity State of Unchanged
Desert Sun Chess Club is located in Phoenix with a Entity State of Deleted
Palm Tree Chess Club is located in San Diego with a Entity State of Unchanged
Clubs Contained in Context Local Collection – After Adding and Deleting
=================
Star City Chess Club is located in New York with a Entity State of Unchanged
Palm Tree Chess Club is located in San Diego with a Entity State of Unchanged
Lonesome Pine is located in Portland with a Entity State of Added


原理

这个示例使用Club实体对象。开始,我们从Local属性公布的动态集合中获取Club实体对象的数量。注意,图5-13没有SQL查询产生,因为一个对Local属性的查询,是不会产生针对数据库的SQL查询的。

图5-13 访问Local动态集合,不会产生一个SQL查询

现在,结果为0,因为我们还没有通过上下文对象执行一个关于Clubs的查询。记住,Local动态集合会自动地与上下文对象保持同步。

接下来,我们通过上下文对象在数据库中查询前面两个Club实体。并循环输出他们的名称和地点。如图5-14所示。

图5-14 查询上下文对象,总是产生一个SQL查询

随后,我们立即循环相应的Local集合,得到了相同的结果。记住,结果集完全一样,因为Local集合自动与DbContext保持同步。如果新的实体被获取到上下文,通过这些实体,Loacal集合会被自动更新。注意,图5-15中,当访问Local集合时,没有SQL查询生成。

图5-15 访问 local集合,没有生成SQL语句

为了进一步演示Local属性的默认行为,我们通过上下文查询获取第三个Club实体,我们又一次循环上下文对象和Local集合,同样得到了相同的结果。图5-16中,通过查询上下文对象的查询产生了一个SQL语句,图5-17中,能通查询Local集合的查询,没有产生任何SQL语句。

图5-16 查询上下文对象,总是产生一个SQL查询

图5-17 访问 local集合,没有生成SQL语句

接下来,我们添加一个名为 Lonesone Pine Club实体到Local集合中,同时,我们从Local集合中删除Desert Sun Club. 然后我们枚举上下文对象中的Clubs,正如期望的那样,一个SQL查询产生了,如图5-18所示。

图5-18 查询上下文对象,总是产生一个SQL查询

有趣的是,我们看到,在上下文中Desert Sun Club已经被标记为删除,但我们没有看到最新添加的Lonesome Pine Club对象。记住,Lonesome Pine Club已经被添加到上下文中,但我们还没有调用SaveChanges()方法更新到底层据库中

然而,当我们枚举Local集合时,没有产生针对底层数据库的查询,如图5-19所示。相反,我们看到了最新添加的Lonesome Pine Club对象,但我们不再看到标记为删除的Desert Sun Club对象。Loacl集合的默认行为是,隐藏任何标记为删除的对象,因为这些对象不于有效

图5-19 访问 local集合,没有生成SQL语句

本质:访问Local集合不会产生针对底层数据库的SQL查询,访问上下中的属性集合总是会产生一个被发送到数据库中的SQL是查询

总之,名为Local的属性公布的实体集,是一个动态的集合(Observale Collection)(译注:也有人译为,可观察的集合,但综合该对象的作用和功能来看,个人认为译为动态集合更恰当一些),它是上下文内容的一个镜像。正如本小节演示的那样,查询Local集合非常有效,因为它不会产生针对底层数据库的SQL查询。

《Entity Framework 6 Recipes》中文翻译系列